[gdm/wip/slave-connection: 26/27] daemon: Add an interface for communicating with GDM from user session



commit 971489e83de28e8604f60f2f72044e2d3844ebd9
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Fri May 18 21:04:21 2012 +0200

    daemon: Add an interface for communicating with GDM from user session
    
    One goal for GNOME 3.6, is to replace the screen locking functionality
    provided by gnome-screensaver with redesigned functionality provided
    by gnome-shell.
    
    At the same time, it makes sense to consolidate the yucky PAM
    authentication code to one place (GDM).
    
    Right now only greeters can talk to GDM.  At the time the greeter is
    started, the slave sets up a private communication channel which the
    greeter then connects to for initiating communication.
    
    This commit adds a new method to the org.gnome.DisplayManager.Display
    interface that allows opening a private connection to the slave that
    is associated with the currently running session.  That slave exports
    an interface similiar to the GdmGreeterServer interface used between
    greeters and slaves, but with some modifications to make it more
    appropriate for communication between the user session and the slave.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=676381

 .gitignore                     |    1 +
 daemon/Makefile.am             |   20 +
 daemon/gdm-dbus-util.c         |   40 ++
 daemon/gdm-dbus-util.h         |    6 +
 daemon/gdm-display.c           |  177 ++++++++-
 daemon/gdm-display.h           |    3 +-
 daemon/gdm-display.xml         |    3 +
 daemon/gdm-greeter-server.c    |   47 +--
 daemon/gdm-greeter-server.h    |    5 +-
 daemon/gdm-greeter-server.xml  |    5 -
 daemon/gdm-session-worker.c    |   11 +
 daemon/gdm-session.h           |    1 +
 daemon/gdm-session.xml         |    1 +
 daemon/gdm-simple-slave.c      |  820 ++++++++++++++++++++++++----------------
 daemon/gdm-slave.c             |   70 ++++
 daemon/gdm-slave.h             |   20 +
 daemon/gdm-slave.xml           |    4 +
 daemon/test-external-greeter.c |  261 +++++++++++++
 daemon/test-session.c          |    3 +-
 data/gdm.conf.in               |   15 +-
 20 files changed, 1121 insertions(+), 392 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 9b0e977..b2e253a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -111,6 +111,7 @@ daemon/gdm-factory-slave
 daemon/gdm-session-worker
 daemon/gdm-simple-slave
 daemon/gdm-xdmcp-chooser-slave
+daemon/test-external-greeter
 daemon/test-session
 data/gdm.schemas.in
 depcomp
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 0fe2f1f..7a608d7 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -105,6 +105,22 @@ gdm-xdmcp-chooser-slave-glue.c gdm-xdmcp-chooser-slave-glue.h: gdm-xdmcp-chooser
 
 noinst_PROGRAMS = 		\
 	test-session		\
+	test-external-greeter	\
+	$(NULL)
+
+test_external_greeter_SOURCES = \
+	test-external-greeter.c	\
+	$(NULL)
+
+nodist_test_external_greeter_SOURCES =	\
+	gdm-display-glue.h		\
+	gdm-display-glue.c		\
+	gdm-greeter-glue.h		\
+	gdm-greeter-glue.c		\
+	$(NULL)
+
+test_external_greeter_LDADD =	\
+	$(DAEMON_LIBS)		\
 	$(NULL)
 
 test_session_SOURCES = 		\
@@ -297,6 +313,8 @@ gdm_binary_SOURCES = 			\
 	gdm-manager.h			\
 	gdm-slave-proxy.c		\
 	gdm-slave-proxy.h		\
+	gdm-dbus-util.c			\
+	gdm-dbus-util.h			\
 	$(NULL)
 
 nodist_gdm_binary_SOURCES = 			\
@@ -308,6 +326,8 @@ nodist_gdm_binary_SOURCES = 			\
 	gdm-transient-display-glue.c		\
 	gdm-static-display-glue.h		\
 	gdm-static-display-glue.c		\
+	gdm-slave-glue.h			\
+	gdm-slave-glue.c			\
 	$(NULL)
 
 XDMCP_SOURCES =				\
diff --git a/daemon/gdm-dbus-util.c b/daemon/gdm-dbus-util.c
index 1dff7a6..03ef1e7 100644
--- a/daemon/gdm-dbus-util.c
+++ b/daemon/gdm-dbus-util.c
@@ -120,3 +120,43 @@ gdm_dbus_setup_private_server (GDBusAuthObserver  *observer,
 
         return server;
 }
+
+gboolean
+gdm_dbus_get_pid_for_name (const char  *system_bus_name,
+                           pid_t       *out_pid,
+                           GError     **error)
+{
+        GDBusConnection *bus;
+        GVariant *reply;
+        gboolean retval = FALSE;
+        unsigned int v;
+
+        bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+        if (bus == NULL) {
+                return FALSE;
+        }
+
+        reply = g_dbus_connection_call_sync (bus,
+                                             "org.freedesktop.DBus",
+                                             "/org/freedesktop/DBus",
+                                             "org.freedesktop.DBus",
+                                             "GetConnectionUnixProcessID",
+                                             g_variant_new ("(s)", system_bus_name),
+                                             G_VARIANT_TYPE ("(u)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL, error);
+        if (reply == NULL) {
+                goto out;
+        }
+
+        g_variant_get (reply, "(u)", &v);
+        *out_pid = v;
+        g_variant_unref (reply);
+
+        retval = TRUE;
+ out:
+        g_object_unref (bus);
+
+        return retval;
+}
diff --git a/daemon/gdm-dbus-util.h b/daemon/gdm-dbus-util.h
index acdb197..d83dea7 100644
--- a/daemon/gdm-dbus-util.h
+++ b/daemon/gdm-dbus-util.h
@@ -22,8 +22,14 @@
 #define __GDM_DBUS_UTIL_H
 
 #include <gio/gio.h>
+#include <unistd.h>
+#include <sys/types.h>
 
 GDBusServer *gdm_dbus_setup_private_server (GDBusAuthObserver  *observer,
 					    GError            **error);
 
+gboolean gdm_dbus_get_pid_for_name (const char  *system_bus_name,
+                                    pid_t       *out_pid,
+                                    GError     **error);
+
 #endif
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c
index 799a51c..4c68fd3 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -33,6 +33,11 @@
 #include <glib/gi18n.h>
 #include <glib-object.h>
 
+#ifdef WITH_SYSTEMD
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-login.h>
+#endif
+
 #include "gdm-display.h"
 #include "gdm-display-glue.h"
 #include "gdm-display-access-file.h"
@@ -41,11 +46,12 @@
 #include "gdm-settings-keys.h"
 
 #include "gdm-slave-proxy.h"
-
-static guint32 display_serial = 1;
+#include "gdm-slave-glue.h"
+#include "gdm-dbus-util.h"
 
 #define GDM_DISPLAY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DISPLAY, GdmDisplayPrivate))
 
+#define GDM_SLAVE_PATH "/org/gnome/DisplayManager/Slave"
 #define DEFAULT_SLAVE_COMMAND LIBEXECDIR "/gdm-simple-slave"
 
 struct GdmDisplayPrivate
@@ -70,6 +76,7 @@ struct GdmDisplayPrivate
 
         GdmSlaveProxy        *slave_proxy;
         char                 *slave_bus_name;
+        GdmDBusSlave         *slave_bus_proxy;
         int                   slave_name_id;
         GDBusConnection      *connection;
         GdmDisplayAccessFile *user_access_file;
@@ -110,20 +117,6 @@ gdm_display_error_quark (void)
         return ret;
 }
 
-static guint32
-get_next_display_serial (void)
-{
-        guint32 serial;
-
-        serial = display_serial++;
-
-        if ((gint32)display_serial < 0) {
-                display_serial = 1;
-        }
-
-        return serial;
-}
-
 time_t
 gdm_display_get_creation_time (GdmDisplay *display)
 {
@@ -308,6 +301,14 @@ gdm_display_real_set_slave_bus_name (GdmDisplay *display,
                                         on_name_vanished,
                                         g_object_ref (display),
                                         NULL);
+
+        g_clear_object (&display->priv->slave_bus_proxy);
+        display->priv->slave_bus_proxy = GDM_DBUS_SLAVE (gdm_dbus_slave_proxy_new_sync (display->priv->connection,
+                                                                                        G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+                                                                                        name,
+                                                                                        GDM_SLAVE_PATH,
+                                                                                        NULL, NULL));
+
         return TRUE;
 }
 
@@ -1176,7 +1177,140 @@ handle_remove_user_authorization (GdmDBusDisplay        *skeleton,
         return TRUE;
 }
 
+#ifdef WITH_SYSTEMD
+static char *
+get_session_id_for_pid_systemd (pid_t    pid,
+                                GError **error)
+{
+        char *session, *gsession;
+        int ret;
+
+        ret = sd_pid_get_session (pid, &session);
+        if (ret < 0) {
+                g_set_error (error,
+                             GDM_DISPLAY_ERROR,
+                             GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+                             "Error getting session id from systemd: %s",
+                             g_strerror (-ret));
+        }
+
+        if (session != NULL) {
+                gsession = g_strdup (session);
+                free (session);
+
+                return gsession;
+        } else {
+                return NULL;
+        }
+}
+#endif
+
+#ifdef WITH_CONSOLEKIT
+static char *
+get_session_id_for_pid_consolekit (GDBusConnection  *connection,
+                                   pid_t             pid,
+                                   GError          **error)
+{
+        GVariant *reply;
+        char *retval;
+
+        reply = g_dbus_connection_call_sync (connection,
+                                             "org.freedesktop.ConsoleKit",
+                                             "/org/freedesktop/ConsoleKit/Manager",
+                                             "org.freedesktop.ConsoleKit.Manager",
+                                             "GetSessionForUnixProcess",
+                                             g_variant_new ("(u)", pid),
+                                             G_VARIANT_TYPE ("(o)"),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL, error);
+        if (reply == NULL) {
+                return NULL;
+        }
 
+        g_variant_get (reply, "(o)", &retval);
+        g_variant_unref (reply);
+
+        return retval;
+}
+#endif
+
+static char *
+get_session_id_for_pid (GdmDisplay  *display,
+                        pid_t        pid,
+                        GError     **error)
+{
+#ifdef WITH_SYSTEMD
+        if (sd_booted () > 0) {
+                return get_session_id_for_pid_systemd (pid, error);
+        }
+#endif
+
+#ifdef WITH_CONSOLEKIT
+        return get_session_id_for_pid_consolekit (display->priv->connection,
+                                                  pid, error);
+#endif
+
+        return NULL;
+}
+
+static gboolean
+handle_connect_to_slave (GdmDBusDisplay        *skeleton,
+                         GDBusMethodInvocation *invocation,
+                         GdmDisplay            *display)
+{
+        GError *error;
+        const char *sender;
+        pid_t pid;
+        char *session_id;
+        char *address;
+        gboolean ok;
+
+        if (display->priv->slave_bus_proxy == NULL) {
+                g_dbus_method_invocation_return_dbus_error (invocation,
+                                                            "org.gnome.DisplayManager.NotReady",
+                                                            "Slave is not yet registered");
+                return TRUE;
+        }
+
+        error = NULL;
+        sender = g_dbus_method_invocation_get_sender (invocation);
+        if (!gdm_dbus_get_pid_for_name (sender,
+                                        &pid,
+                                        &error)) {
+                g_prefix_error (&error, "Error while retrieving caller session id: ");
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+
+                return TRUE;
+        }
+
+        session_id = get_session_id_for_pid (display,
+                                             pid,
+                                             &error);
+        if (session_id == NULL) {
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+                return TRUE;
+        }
+
+        address = NULL;
+        ok = gdm_dbus_slave_call_get_private_connection_sync (display->priv->slave_bus_proxy,
+                                                              session_id,
+                                                              &address,
+                                                              NULL, &error);
+        if (!ok) {
+                g_dbus_method_invocation_return_gerror (invocation, error);
+                g_error_free (error);
+        } else {
+                gdm_dbus_display_complete_connect_to_slave (skeleton, invocation, address);
+        }
+
+        g_free (address);
+        g_free (session_id);
+
+        return TRUE;
+}
 
 static gboolean
 register_display (GdmDisplay *display)
@@ -1220,6 +1354,8 @@ register_display (GdmDisplay *display)
                           G_CALLBACK (handle_add_user_authorization), display);
         g_signal_connect (display->priv->display_skeleton, "handle-remove-user-authorization",
                           G_CALLBACK (handle_remove_user_authorization), display);
+        g_signal_connect (display->priv->display_skeleton, "handle-connect-to-slave",
+                          G_CALLBACK (handle_connect_to_slave), display);
 
         g_dbus_object_skeleton_add_interface (display->priv->object_skeleton,
                                               G_DBUS_INTERFACE_SKELETON (display->priv->display_skeleton));
@@ -1237,14 +1373,21 @@ gdm_display_constructor (GType                  type,
                          GObjectConstructParam *construct_properties)
 {
         GdmDisplay      *display;
+        char            *canonical_display_name;
         gboolean         res;
 
         display = GDM_DISPLAY (G_OBJECT_CLASS (gdm_display_parent_class)->constructor (type,
                                                                                        n_construct_properties,
                                                                                        construct_properties));
 
+        canonical_display_name = g_strdelimit (g_strdup (display->priv->x11_display_name),
+                                               ":" G_STR_DELIMITERS, '_');
+
         g_free (display->priv->id);
-        display->priv->id = g_strdup_printf ("/org/gnome/DisplayManager/Displays/%u", get_next_display_serial ());
+        display->priv->id = g_strdup_printf ("/org/gnome/DisplayManager/Displays/%s",
+                                             canonical_display_name);
+
+        g_free (canonical_display_name);
 
         res = register_display (display);
         if (! res) {
diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h
index 6cbcca2..f7a26c5 100644
--- a/daemon/gdm-display.h
+++ b/daemon/gdm-display.h
@@ -79,7 +79,8 @@ typedef struct
 typedef enum
 {
          GDM_DISPLAY_ERROR_GENERAL,
-         GDM_DISPLAY_ERROR_GETTING_USER_INFO
+         GDM_DISPLAY_ERROR_GETTING_USER_INFO,
+         GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
 } GdmDisplayError;
 
 #define GDM_DISPLAY_ERROR gdm_display_error_quark ()
diff --git a/daemon/gdm-display.xml b/daemon/gdm-display.xml
index 13c734d..6c23d73 100644
--- a/daemon/gdm-display.xml
+++ b/daemon/gdm-display.xml
@@ -43,5 +43,8 @@
       <arg name="username" direction="out" type="s"/>
       <arg name="delay" direction="out" type="i"/>
     </method>
+    <method name="ConnectToSlave">
+      <arg name="address" direction="out" type="s"/>
+    </method>
   </interface>
 </node>
diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c
index f9d1643..eaf9e10 100644
--- a/daemon/gdm-greeter-server.c
+++ b/daemon/gdm-greeter-server.c
@@ -172,16 +172,6 @@ gdm_greeter_server_problem (GdmGreeterServer *greeter_server,
 }
 
 gboolean
-gdm_greeter_server_authentication_failed (GdmGreeterServer *greeter_server,
-                                          const char       *service_name)
-{
-        service_name = translate_outgoing_service_name (greeter_server, service_name);
-        gdm_dbus_greeter_server_emit_authentication_failed (greeter_server->priv->skeleton,
-                                                            service_name);
-        return TRUE;
-}
-
-gboolean
 gdm_greeter_server_service_unavailable (GdmGreeterServer *greeter_server,
                                         const char       *service_name)
 {
@@ -423,18 +413,6 @@ handle_cancel (GdmDBusGreeterServer  *skeleton,
 }
 
 static gboolean
-handle_disconnect (GdmDBusGreeterServer  *skeleton,
-                   GDBusMethodInvocation *invocation,
-                   GdmGreeterServer      *greeter_server)
-{
-        g_dbus_method_invocation_return_value (invocation, NULL);
-
-        g_signal_emit (greeter_server, signals [DISCONNECTED], 0);
-
-        return TRUE;
-}
-
-static gboolean
 handle_get_display_id (GdmDBusGreeterServer  *skeleton,
                        GDBusMethodInvocation *invocation,
                        GdmGreeterServer      *greeter_server)
@@ -480,6 +458,8 @@ connection_closed (GDBusConnection *connection,
                  remote_peer_vanished ? "yes" : "no");
 
         g_clear_object(&greeter_server->priv->greeter_connection);
+
+        g_signal_emit (greeter_server, signals [DISCONNECTED], 0);
 }
 
 static gboolean
@@ -541,8 +521,6 @@ handle_connection (GDBusServer      *server,
                                   G_CALLBACK (handle_select_user), greeter_server);
                 g_signal_connect (greeter_server->priv->skeleton, "handle-cancel",
                                   G_CALLBACK (handle_cancel), greeter_server);
-                g_signal_connect (greeter_server->priv->skeleton, "handle-disconnect",
-                                  G_CALLBACK (handle_disconnect), greeter_server);
                 g_signal_connect (greeter_server->priv->skeleton, "handle-get-display-id",
                                   G_CALLBACK (handle_get_display_id), greeter_server);
                 g_signal_connect (greeter_server->priv->skeleton, "handle-start-session-when-ready",
@@ -562,7 +540,8 @@ handle_connection (GDBusServer      *server,
 }
 
 gboolean
-gdm_greeter_server_start (GdmGreeterServer *greeter_server)
+gdm_greeter_server_start (GdmGreeterServer *greeter_server,
+                          gboolean          allow_any_user)
 {
         GError *error = NULL;
         gboolean ret;
@@ -572,14 +551,18 @@ gdm_greeter_server_start (GdmGreeterServer *greeter_server)
 
         g_debug ("GreeterServer: Creating D-Bus server for greeter");
 
-        observer = g_dbus_auth_observer_new ();
-        g_signal_connect_object (observer, "authorize-authenticated-peer",
-                                 G_CALLBACK (allow_user_function), greeter_server, 0);
+        observer = NULL;
+        if (!allow_any_user) {
+                observer = g_dbus_auth_observer_new ();
+                g_signal_connect_object (observer, "authorize-authenticated-peer",
+                                         G_CALLBACK (allow_user_function), greeter_server, 0);
+        }
 
         greeter_server->priv->server = gdm_dbus_setup_private_server (observer,
                                                                       &error);
 
-        g_object_unref (observer);
+        if (observer)
+                g_object_unref (observer);
 
         if (greeter_server->priv->server == NULL) {
                 g_warning ("Cannot create D-BUS server for the greeter: %s", error->message);
@@ -607,8 +590,10 @@ gdm_greeter_server_stop (GdmGreeterServer *greeter_server)
 {
         g_debug ("GreeterServer: Stopping greeter server...");
 
-        g_dbus_server_stop (greeter_server->priv->server);
-        g_clear_object (&greeter_server->priv->server);
+        if (greeter_server->priv->server) {
+                g_dbus_server_stop (greeter_server->priv->server);
+                g_clear_object (&greeter_server->priv->server);
+        }
 
         g_clear_object (&greeter_server->priv->greeter_connection);
         g_clear_object (&greeter_server->priv->skeleton);
diff --git a/daemon/gdm-greeter-server.h b/daemon/gdm-greeter-server.h
index c4c66a1..ce760d9 100644
--- a/daemon/gdm-greeter-server.h
+++ b/daemon/gdm-greeter-server.h
@@ -75,7 +75,8 @@ typedef struct
 GType               gdm_greeter_server_get_type              (void);
 GdmGreeterServer *  gdm_greeter_server_new                   (const char       *display_id);
 
-gboolean            gdm_greeter_server_start                 (GdmGreeterServer *greeter_server);
+gboolean            gdm_greeter_server_start                 (GdmGreeterServer *greeter_server,
+                                                              gboolean          allow_any_user);
 gboolean            gdm_greeter_server_stop                  (GdmGreeterServer *greeter_server);
 char *              gdm_greeter_server_get_address           (GdmGreeterServer *greeter_server);
 
@@ -91,8 +92,6 @@ gboolean            gdm_greeter_server_info                  (GdmGreeterServer *
 gboolean            gdm_greeter_server_problem               (GdmGreeterServer *greeter_server,
                                                               const char       *service_name,
                                                               const char       *text);
-gboolean            gdm_greeter_server_authentication_failed (GdmGreeterServer *greeter_server,
-                                                              const char       *service_name);
 gboolean            gdm_greeter_server_service_unavailable   (GdmGreeterServer *greeter_server,
                                                               const char       *service_name);
 gboolean            gdm_greeter_server_reset                 (GdmGreeterServer *greeter_server);
diff --git a/daemon/gdm-greeter-server.xml b/daemon/gdm-greeter-server.xml
index 537af8f..26cfd18 100644
--- a/daemon/gdm-greeter-server.xml
+++ b/daemon/gdm-greeter-server.xml
@@ -33,8 +33,6 @@
     </method>
     <method name="Cancel">
     </method>
-    <method name="Disconnect">
-    </method>
     <method name="GetDisplayId">
       <arg name="id" direction="out" type="o"/>
     </method>
@@ -79,9 +77,6 @@
     </signal>
     <signal name="Reset">
     </signal>
-    <signal name="AuthenticationFailed">
-      <arg name="service_name" type="s"/>
-    </signal>
     <signal name="SessionOpened">
       <arg name="service_name" type="s"/>
     </signal>
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 1922920..7a956cc 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -122,6 +122,7 @@ struct GdmSessionWorkerPrivate
         char             *username;
         char             *log_file;
         char             *session_type;
+        char             *session_id;
         uid_t             uid;
         gid_t             gid;
         gboolean          password_is_required;
@@ -1812,6 +1813,7 @@ gdm_session_worker_open_session (GdmSessionWorker  *worker,
 {
         int error_code;
         int flags;
+        const char *session_id;
 
         g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED);
         g_assert (geteuid () == 0);
@@ -1835,6 +1837,13 @@ gdm_session_worker_open_session (GdmSessionWorker  *worker,
         g_debug ("GdmSessionWorker: state SESSION_OPENED");
         worker->priv->state = GDM_SESSION_WORKER_STATE_SESSION_OPENED;
 
+        session_id = gdm_session_worker_get_environment_variable (worker, "XDG_SESSION_ID");
+
+        if (session_id != NULL) {
+                g_free (worker->priv->session_id);
+                worker->priv->session_id = session_id;
+        }
+
  out:
         if (error_code != PAM_SUCCESS) {
                 gdm_session_worker_uninitialize_pam (worker, error_code);
@@ -2209,6 +2218,8 @@ do_start_session (GdmSessionWorker *worker)
 
         gdm_dbus_session_call_session_started_sync (worker->priv->session_proxy,
                                                     worker->priv->service,
+                                                    worker->priv->session_id ?
+                                                    worker->priv->session_id : "",
                                                     worker->priv->child_pid,
                                                     NULL, NULL);
 }
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index b716cbe..f967c45 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -124,6 +124,7 @@ GdmSession      *gdm_session_new                      (const char *display_id,
 char             *gdm_session_get_username                (GdmSession     *session);
 char             *gdm_session_get_display_device          (GdmSession     *session);
 char             *gdm_session_get_display_seat_id         (GdmSession     *session);
+char             *gdm_session_get_session_id              (GdmSession     *session);
 gboolean          gdm_session_bypasses_xsession           (GdmSession     *session);
 
 void              gdm_session_start_conversation          (GdmSession *session,
diff --git a/daemon/gdm-session.xml b/daemon/gdm-session.xml
index bfc4098..ffdb97e 100644
--- a/daemon/gdm-session.xml
+++ b/daemon/gdm-session.xml
@@ -87,6 +87,7 @@
     </method>
     <method name="SessionStarted">
       <arg name="service_name" direction="in" type="s"/>
+      <arg name="session_id" direction="in" type="s"/>
       <arg name="pid" direction="in" type="i"/>
     </method>
     <method name="SessionStartFailed">
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 6968017..99b7794 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -62,13 +62,21 @@
 #define MAX_CONNECT_ATTEMPTS  10
 #define DEFAULT_PING_INTERVAL 15
 
+/* Each of these structs represents a login session */
+typedef struct {
+        GdmSimpleSlave    *slave;
+        GdmGreeterServer  *greeter_server;
+        GdmSession        *session;
+
+        /* Only valid for the primary session */
+        gint               greeter_reset_id;
+        guint              start_session_id;
+} GdmSlaveSession;
+
 struct GdmSimpleSlavePrivate
 {
         GPid               pid;
 
-        guint              greeter_reset_id;
-        guint              start_session_id;
-
         char              *start_session_service_name;
 
         int                ping_interval;
@@ -77,9 +85,8 @@ struct GdmSimpleSlavePrivate
         guint              connection_attempts;
 
         GdmServer         *server;
-        GdmSession        *session;
-
-        GdmGreeterServer  *greeter_server;
+        GdmSlaveSession   *primary_session;
+        GList             *other_sessions;
         GdmGreeterSession *greeter;
 
         guint              start_session_when_ready : 1;
@@ -102,25 +109,46 @@ static void     gdm_simple_slave_finalize       (GObject             *object);
 
 G_DEFINE_TYPE (GdmSimpleSlave, gdm_simple_slave, GDM_TYPE_SLAVE)
 
-static void create_new_session (GdmSimpleSlave *slave);
-static void destroy_session    (GdmSimpleSlave *slave);
+static void create_new_session (GdmSlaveSession *slave_session);
+static void destroy_session    (GdmSlaveSession *slave_session);
 static void start_greeter      (GdmSimpleSlave *slave);
-static void start_session      (GdmSimpleSlave *slave);
+static void start_session      (GdmSlaveSession *slave_session);
 static void queue_start_session (GdmSimpleSlave *slave,
                                  const char     *service_name);
 
 static void
+free_slave_session (GdmSlaveSession *slave_session)
+{
+        if (slave_session->greeter_server) {
+                gdm_greeter_server_stop (slave_session->greeter_server);
+                g_object_unref (slave_session->greeter_server);
+        }
+
+        if (slave_session->session) {
+                gdm_session_close (slave_session->session);
+                g_object_unref (slave_session->session);
+        }
+
+        g_slice_free (GdmSlaveSession, slave_session);
+}
+
+static void
 on_session_started (GdmSession       *session,
                     const char       *service_name,
                     int               pid,
-                    GdmSimpleSlave   *slave)
+                    GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave;
         char *username;
 
         g_debug ("GdmSimpleSlave: session started %d", pid);
 
+        g_assert (slave_session == slave_session->slave->priv->primary_session);
+
+        slave = slave_session->slave;
+
         /* Run the PreSession script. gdmslave suspends until script has terminated */
-        username = gdm_session_get_username (slave->priv->session);
+        username = gdm_session_get_username (slave->priv->primary_session->session);
         if (username != NULL) {
                 gdm_slave_run_script (GDM_SLAVE (slave), GDMCONFDIR "/PreSession", username);
         }
@@ -207,8 +235,10 @@ gdm_simple_slave_revoke_console_permissions (GdmSimpleSlave *slave)
 static void
 on_session_exited (GdmSession       *session,
                    int               exit_code,
-                   GdmSimpleSlave   *slave)
+                   GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave = slave_session->slave;
+
         g_debug ("GdmSimpleSlave: session exited with code %d\n", exit_code);
         if (slave->priv->start_session_service_name == NULL) {
                 gdm_slave_stopped (GDM_SLAVE (slave));
@@ -218,8 +248,10 @@ on_session_exited (GdmSession       *session,
 static void
 on_session_died (GdmSession       *session,
                  int               signal_number,
-                 GdmSimpleSlave   *slave)
+                 GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave = slave_session->slave;
+
         g_debug ("GdmSimpleSlave: session died with signal %d, (%s)",
                  signal_number,
                  g_strsignal (signal_number));
@@ -236,7 +268,7 @@ add_user_authorization (GdmSimpleSlave *slave,
         char    *username;
         gboolean ret;
 
-        username = gdm_session_get_username (slave->priv->session);
+        username = gdm_session_get_username (slave->priv->primary_session->session);
         ret = gdm_slave_add_user_authorization (GDM_SLAVE (slave),
                                                 username,
                                                 filename);
@@ -246,45 +278,45 @@ add_user_authorization (GdmSimpleSlave *slave,
 }
 
 static void
-reset_session (GdmSimpleSlave *slave)
+reset_session (GdmSlaveSession *slave_session)
 {
-        destroy_session (slave);
-        create_new_session (slave);
+        destroy_session (slave_session);
+        create_new_session (slave_session);
 }
 
 static gboolean
-greeter_reset_timeout (GdmSimpleSlave *slave)
+greeter_reset_timeout (GdmSlaveSession *slave_session)
 {
         g_debug ("GdmSimpleSlave: resetting greeter");
 
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_reset (slave->priv->greeter_server);
-                reset_session (slave);
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_reset (slave_session->greeter_server);
+                reset_session (slave_session);
         } else {
-                start_greeter (slave);
-                create_new_session (slave);
+                start_greeter (slave_session->slave);
+                create_new_session (slave_session);
         }
-        slave->priv->greeter_reset_id = 0;
+        slave_session->greeter_reset_id = 0;
         return FALSE;
 }
 
 static void
-queue_greeter_reset (GdmSimpleSlave *slave)
+queue_greeter_reset (GdmSlaveSession *slave_session)
 {
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
 
-        slave->priv->greeter_reset_id = g_idle_add ((GSourceFunc)greeter_reset_timeout, slave);
+        slave_session->greeter_reset_id = g_idle_add ((GSourceFunc)greeter_reset_timeout, slave_session);
 }
 
 static void
 on_session_service_unavailable (GdmSession       *session,
                                 const char       *service_name,
-                                GdmSimpleSlave   *slave)
+                                GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_service_unavailable (slave->priv->greeter_server,
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_service_unavailable (slave_session->greeter_server,
                                                         service_name);
         }
 
@@ -294,7 +326,7 @@ on_session_service_unavailable (GdmSession       *session,
 static void
 on_session_setup_complete (GdmSession       *session,
                            const char       *service_name,
-                           GdmSimpleSlave   *slave)
+                           GdmSlaveSession  *slave_session)
 {
         gdm_session_authenticate (session, service_name);
 }
@@ -303,10 +335,10 @@ static void
 on_session_setup_failed (GdmSession       *session,
                          const char       *service_name,
                          const char       *message,
-                         GdmSimpleSlave   *slave)
+                         GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_problem (slave->priv->greeter_server,
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_problem (slave_session->greeter_server,
                                             service_name,
                                             message != NULL ? message:  _("Unable to initialize login system"));
         }
@@ -317,7 +349,7 @@ on_session_setup_failed (GdmSession       *session,
 static void
 on_session_authenticated (GdmSession       *session,
                           const char       *service_name,
-                          GdmSimpleSlave   *slave)
+                          GdmSlaveSession  *slave_session)
 {
         gdm_session_authorize (session, service_name);
 }
@@ -326,10 +358,10 @@ static void
 on_session_authentication_failed (GdmSession       *session,
                                   const char       *service_name,
                                   const char       *message,
-                                  GdmSimpleSlave   *slave)
+                                  GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_problem (slave->priv->greeter_server,
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_problem (slave_session->greeter_server,
                                             service_name,
                                             message != NULL ? message : _("Unable to authenticate user"));
         }
@@ -353,20 +385,27 @@ gdm_simple_slave_start_session_when_ready (GdmSimpleSlave *slave,
 static void
 on_session_authorized (GdmSession       *session,
                        const char       *service_name,
-                       GdmSimpleSlave   *slave)
+                       GdmSlaveSession  *slave_session)
 {
-        /* FIXME: we don't yet support refresh */
-        gdm_session_accredit (slave->priv->session, service_name, FALSE);
+        gboolean refresh;
+
+        if (slave_session != slave_session->slave->priv->primary_session) {
+                refresh = TRUE;
+        } else {
+                refresh = FALSE;
+        }
+
+        gdm_session_accredit (session, service_name, refresh);
 }
 
 static void
 on_session_authorization_failed (GdmSession       *session,
                                  const char       *service_name,
                                  const char       *message,
-                                 GdmSimpleSlave   *slave)
+                                 GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_problem (slave->priv->greeter_server,
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_problem (slave_session->greeter_server,
                                             service_name,
                                             message != NULL ? message :  _("Unable to authorize user"));
         }
@@ -375,17 +414,17 @@ on_session_authorization_failed (GdmSession       *session,
 }
 
 static gboolean
-try_migrate_session (GdmSimpleSlave *slave)
+try_migrate_session (GdmSlaveSession *slave_session)
 {
         char    *username;
         gboolean res;
 
         g_debug ("GdmSimpleSlave: trying to migrate session");
 
-        username = gdm_session_get_username (slave->priv->session);
+        username = gdm_session_get_username (slave_session->session);
 
         /* try to switch to an existing session */
-        res = gdm_slave_switch_to_user_session (GDM_SLAVE (slave), username);
+        res = gdm_slave_switch_to_user_session (GDM_SLAVE (slave_session->slave), username);
         g_free (username);
 
         return res;
@@ -405,8 +444,8 @@ stop_greeter (GdmSimpleSlave *slave)
 
         /* Run the PostLogin script. gdmslave suspends until script has terminated */
         username = NULL;
-        if (slave->priv->session != NULL) {
-                username = gdm_session_get_username (slave->priv->session);
+        if (slave->priv->primary_session->session != NULL) {
+                username = gdm_session_get_username (slave->priv->primary_session->session);
         }
 
         if (username != NULL) {
@@ -418,57 +457,62 @@ stop_greeter (GdmSimpleSlave *slave)
 }
 
 static void
-start_session (GdmSimpleSlave *slave)
+start_session (GdmSlaveSession *slave_session)
 {
+        GdmSimpleSlave *slave;
         char    *auth_file;
+
+        slave = slave_session->slave;
         auth_file = NULL;
         add_user_authorization (slave, &auth_file);
 
         g_assert (auth_file != NULL);
 
-        g_object_set (slave->priv->session,
+        g_object_set (slave_session->session,
                       "user-x11-authority-file", auth_file,
                       NULL);
 
         g_free (auth_file);
 
-        gdm_session_start_session (slave->priv->session,
-                                          slave->priv->start_session_service_name);
+        gdm_session_start_session (slave_session->session,
+                                   slave->priv->start_session_service_name);
 
-        slave->priv->start_session_id = 0;
+        slave_session->start_session_id = 0;
         g_free (slave->priv->start_session_service_name);
         slave->priv->start_session_service_name = NULL;
 }
 
 static gboolean
-start_session_timeout (GdmSimpleSlave *slave)
+start_session_timeout (GdmSlaveSession *slave_session)
 {
-
+        GdmSimpleSlave *slave;
         gboolean migrated;
 
+        slave = slave_session->slave;
+
         g_debug ("GdmSimpleSlave: accredited");
 
-        migrated = try_migrate_session (slave);
+        migrated = try_migrate_session (slave_session);
         g_debug ("GdmSimpleSlave: migrated: %d", migrated);
         if (migrated) {
-                destroy_session (slave);
+                destroy_session (slave_session);
 
                 /* We don't stop the slave here because
                    when Xorg exits it switches to the VT it was
                    started from.  That interferes with fast
                    user switching. */
-                queue_greeter_reset (slave);
+                queue_greeter_reset (slave_session);
 
-                slave->priv->start_session_id = 0;
+                slave_session->start_session_id = 0;
                 g_free (slave->priv->start_session_service_name);
                 slave->priv->start_session_service_name = NULL;
         } else {
                 if (slave->priv->greeter == NULL) {
                         /* auto login */
-                        start_session (slave);
+                        start_session (slave_session);
                 } else {
                         /* Session actually gets started from on_greeter_session_stop */
-                        stop_greeter (slave);
+                        stop_greeter (slave_session->slave);
                 }
         }
 
@@ -479,45 +523,63 @@ static void
 queue_start_session (GdmSimpleSlave *slave,
                      const char     *service_name)
 {
-        if (slave->priv->start_session_id > 0) {
+        GdmSlaveSession *slave_session = slave->priv->primary_session;
+
+        if (slave_session->start_session_id > 0) {
                 return;
         }
 
-        slave->priv->start_session_id = g_idle_add ((GSourceFunc)start_session_timeout, slave);
+        slave_session->start_session_id = g_idle_add ((GSourceFunc)start_session_timeout, slave_session);
         slave->priv->start_session_service_name = g_strdup (service_name);
 }
 
 static void
 on_session_accredited (GdmSession       *session,
                        const char       *service_name,
-                       GdmSimpleSlave   *slave)
+                       GdmSlaveSession  *slave_session)
 {
-        gdm_session_open_session (session, service_name);
+        if (slave_session == slave_session->slave->priv->primary_session) {
+                gdm_session_open_session (session, service_name);
+        } else {
+                /* For sessions that are not under slave's control,
+                   don't actually open a session, just report it happened
+                   and wait for disconnection from the greeter.
+                */
+                gdm_greeter_server_session_opened (slave_session->greeter_server,
+                                                   service_name);
+        }
 }
 
 static void
 on_session_accreditation_failed (GdmSession       *session,
                                  const char       *service_name,
                                  const char       *message,
-                                 GdmSimpleSlave   *slave)
+                                 GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave;
         gboolean migrated;
 
         g_debug ("GdmSimpleSlave: accreditation failed");
 
-        migrated = try_migrate_session (slave);
+        slave = slave_session->slave;
+
+        /* Only try migration if slave_session is the primary session */
+        if (slave_session == slave->priv->primary_session)
+                migrated = try_migrate_session (slave_session);
+        else
+                migrated = FALSE;
 
         /* If we switched to another session we don't care if
            accreditation fails */
         if (! migrated) {
-                if (slave->priv->greeter_server != NULL) {
+                if (slave_session->greeter_server != NULL) {
                         const char *problem;
                         if (message) {
                                 problem = message;
                         } else {
                                 problem = _("Unable to establish credentials");
                         }
-                        gdm_greeter_server_problem (slave->priv->greeter_server,
+                        gdm_greeter_server_problem (slave_session->greeter_server,
                                                     service_name,
                                                     problem);
                 }
@@ -529,14 +591,16 @@ on_session_accreditation_failed (GdmSession       *session,
 static void
 on_session_opened (GdmSession       *session,
                    const char       *service_name,
-                   GdmSimpleSlave   *slave)
+                   GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave = slave_session->slave;
+
 #ifdef  HAVE_LOGINDEVPERM
         gdm_simple_slave_grant_console_permissions (slave);
 #endif  /* HAVE_LOGINDEVPERM */
 
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_session_opened (slave->priv->greeter_server, service_name);
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_session_opened (slave_session->greeter_server, service_name);
                 gdm_simple_slave_start_session_when_ready (slave, service_name);
         } else {
                 slave->priv->start_session_when_ready = TRUE;
@@ -548,10 +612,10 @@ static void
 on_session_open_failed (GdmSession       *session,
                         const char       *service_name,
                         const char       *message,
-                        GdmSimpleSlave   *slave)
+                        GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_problem (slave->priv->greeter_server,
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_problem (slave_session->greeter_server,
                                             service_name,
                                             _("Unable to open session"));
         }
@@ -563,11 +627,11 @@ static void
 on_session_info (GdmSession       *session,
                  const char       *service_name,
                  const char       *text,
-                 GdmSimpleSlave   *slave)
+                 GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Info: %s", text);
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_info (slave->priv->greeter_server, service_name, text);
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_info (slave_session->greeter_server, service_name, text);
         }
 }
 
@@ -575,80 +639,85 @@ static void
 on_session_problem (GdmSession       *session,
                     const char       *service_name,
                     const char       *text,
-                    GdmSimpleSlave   *slave)
+                    GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Problem: %s", text);
-        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, text);
+        gdm_greeter_server_problem (slave_session->greeter_server, service_name, text);
 }
 
 static void
 on_session_info_query (GdmSession       *session,
                        const char       *service_name,
                        const char       *text,
-                       GdmSimpleSlave   *slave)
+                       GdmSlaveSession  *slave_session)
 {
 
         g_debug ("GdmSimpleSlave: Info query: %s", text);
-        gdm_greeter_server_info_query (slave->priv->greeter_server, service_name, text);
+        gdm_greeter_server_info_query (slave_session->greeter_server, service_name, text);
 }
 
 static void
 on_session_secret_info_query (GdmSession       *session,
                               const char       *service_name,
                               const char       *text,
-                              GdmSimpleSlave   *slave)
+                              GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Secret info query: %s", text);
-        gdm_greeter_server_secret_info_query (slave->priv->greeter_server, service_name, text);
+        gdm_greeter_server_secret_info_query (slave_session->greeter_server, service_name, text);
 }
 
 static void
 on_session_conversation_started (GdmSession       *session,
                                  const char       *service_name,
-                                 GdmSimpleSlave   *slave)
+                                 GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave;
         gboolean res;
         gboolean enabled;
         char    *username;
         int      delay;
 
+        slave = slave_session->slave;
+
         g_debug ("GdmSimpleSlave: session conversation started");
-        if (slave->priv->greeter_server != NULL) {
-                res = gdm_greeter_server_ready (slave->priv->greeter_server,
+        if (slave_session->greeter_server != NULL) {
+                res = gdm_greeter_server_ready (slave_session->greeter_server,
                                                 service_name);
                 if (! res) {
                         g_warning ("Unable to send ready");
                 }
         }
 
-        enabled = FALSE;
-        gdm_slave_get_timed_login_details (GDM_SLAVE (slave), &enabled, &username, &delay);
-        if (! enabled) {
-                return;
-        }
+        if (slave_session == slave->priv->primary_session) {
+                enabled = FALSE;
+                gdm_slave_get_timed_login_details (GDM_SLAVE (slave), &enabled, &username, &delay);
+                if (! enabled) {
+                        return;
+                }
 
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_request_timed_login (slave->priv->greeter_server, username, delay);
-        } else {
-                g_debug ("GdmSimpleSlave: begin auto login for user '%s'", username);
-                /* service_name will be "gdm-autologin"
-                 */
-                gdm_session_setup_for_user (slave->priv->session, service_name, username);
-        }
+                if (slave_session->greeter_server != NULL) {
+                        gdm_greeter_server_request_timed_login (slave_session->greeter_server, username, delay);
+                } else {
+                        g_debug ("GdmSimpleSlave: begin auto login for user '%s'", username);
+                        /* service_name will be "gdm-autologin"
+                         */
+                        gdm_session_setup_for_user (slave_session->session, service_name, username);
+                }
 
-        g_free (username);
+                g_free (username);
+        }
 }
 
 static void
 on_session_conversation_stopped (GdmSession       *session,
                                  const char       *service_name,
-                                 GdmSimpleSlave   *slave)
+                                 GdmSlaveSession  *slave_session)
 {
         gboolean res;
         g_debug ("GdmSimpleSlave: conversation stopped");
 
-        if (slave->priv->greeter != NULL) {
-                res = gdm_greeter_server_conversation_stopped (slave->priv->greeter_server,
+        if (slave_session->greeter_server != NULL) {
+                res = gdm_greeter_server_conversation_stopped (slave_session->greeter_server,
                                                                service_name);
                 if (! res) {
                         g_warning ("Unable to send conversation stopped");
@@ -659,43 +728,51 @@ on_session_conversation_stopped (GdmSession       *session,
 static void
 on_session_selected_user_changed (GdmSession       *session,
                                   const char       *text,
-                                  GdmSimpleSlave   *slave)
+                                  GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Selected user changed: %s", text);
 
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_selected_user_changed (slave->priv->greeter_server, text);
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_selected_user_changed (slave_session->greeter_server, text);
         }
 }
 
 static void
 on_default_language_name_changed (GdmSession       *session,
                                   const char       *text,
-                                  GdmSimpleSlave   *slave)
+                                  GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Default language name changed: %s", text);
 
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_default_language_name_changed (slave->priv->greeter_server, text);
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_default_language_name_changed (slave_session->greeter_server, text);
         }
 }
 
 static void
 on_default_session_name_changed (GdmSession       *session,
                                  const char       *text,
-                                 GdmSimpleSlave   *slave)
+                                 GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Default session name changed: %s", text);
 
-        if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_default_session_name_changed (slave->priv->greeter_server, text);
+        if (slave_session->greeter_server != NULL) {
+                gdm_greeter_server_default_session_name_changed (slave_session->greeter_server, text);
         }
 }
 
 static void
-start_autologin_conversation_if_necessary (GdmSimpleSlave *slave)
+start_autologin_conversation_if_necessary (GdmSlaveSession *slave_session)
 {
+        GdmSimpleSlave *slave;
         gboolean enabled;
+
+        slave = slave_session->slave;
+
+        /* Only do autologin for the primary session */
+        if (slave_session != slave->priv->primary_session)
+                return;
+
         gdm_slave_get_timed_login_details (GDM_SLAVE (slave), &enabled, NULL, NULL);
 
         if (!enabled) {
@@ -703,11 +780,11 @@ start_autologin_conversation_if_necessary (GdmSimpleSlave *slave)
         }
 
         g_debug ("GdmSimpleSlave: Starting automatic login conversation");
-        gdm_session_start_conversation (slave->priv->session, "gdm-autologin");
+        gdm_session_start_conversation (slave_session->session, "gdm-autologin");
 }
 
 static void
-create_new_session (GdmSimpleSlave *slave)
+create_new_session (GdmSlaveSession *slave_session)
 {
         gboolean       display_is_local;
         char          *display_id;
@@ -719,7 +796,7 @@ create_new_session (GdmSimpleSlave *slave)
 
         g_debug ("GdmSimpleSlave: Creating new session");
 
-        g_object_get (slave,
+        g_object_get (slave_session->slave,
                       "display-id", &display_id,
                       "display-name", &display_name,
                       "display-hostname", &display_hostname,
@@ -729,201 +806,202 @@ create_new_session (GdmSimpleSlave *slave)
                       NULL);
 
         display_device = NULL;
-        if (slave->priv->server != NULL) {
-                display_device = gdm_server_get_display_device (slave->priv->server);
+        if (slave_session->slave->priv->server != NULL) {
+                display_device = gdm_server_get_display_device (slave_session->slave->priv->server);
         }
 
-        slave->priv->session = gdm_session_new (display_id,
-                                                display_name,
-                                                display_hostname,
-                                                display_device,
-                                                display_seat_id,
-                                                display_x11_authority_file,
-                                                display_is_local);
+        slave_session->session = gdm_session_new (display_id,
+                                                  display_name,
+                                                  display_hostname,
+                                                  display_device,
+                                                  display_seat_id,
+                                                  display_x11_authority_file,
+                                                  display_is_local);
         g_free (display_id);
         g_free (display_name);
         g_free (display_device);
         g_free (display_hostname);
 
-        g_signal_connect (slave->priv->session,
+        g_signal_connect (slave_session->session,
                           "conversation-started",
                           G_CALLBACK (on_session_conversation_started),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "conversation-stopped",
                           G_CALLBACK (on_session_conversation_stopped),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "service-unavailable",
                           G_CALLBACK (on_session_service_unavailable),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "setup-complete",
                           G_CALLBACK (on_session_setup_complete),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "setup-failed",
                           G_CALLBACK (on_session_setup_failed),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "authenticated",
                           G_CALLBACK (on_session_authenticated),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "authentication-failed",
                           G_CALLBACK (on_session_authentication_failed),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "authorized",
                           G_CALLBACK (on_session_authorized),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "authorization-failed",
                           G_CALLBACK (on_session_authorization_failed),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "accredited",
                           G_CALLBACK (on_session_accredited),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "accreditation-failed",
                           G_CALLBACK (on_session_accreditation_failed),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "session-opened",
                           G_CALLBACK (on_session_opened),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "session-open-failed",
                           G_CALLBACK (on_session_open_failed),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "info",
                           G_CALLBACK (on_session_info),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "problem",
                           G_CALLBACK (on_session_problem),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "info-query",
                           G_CALLBACK (on_session_info_query),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "secret-info-query",
                           G_CALLBACK (on_session_secret_info_query),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "session-started",
                           G_CALLBACK (on_session_started),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "session-exited",
                           G_CALLBACK (on_session_exited),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "session-died",
                           G_CALLBACK (on_session_died),
-                          slave);
-        g_signal_connect (slave->priv->session,
+                          slave_session);
+        g_signal_connect (slave_session->session,
                           "selected-user-changed",
                           G_CALLBACK (on_session_selected_user_changed),
-                          slave);
+                          slave_session);
 
-        g_signal_connect (slave->priv->session,
+        g_signal_connect (slave_session->session,
                           "default-language-name-changed",
                           G_CALLBACK (on_default_language_name_changed),
-                          slave);
+                          slave_session);
 
-        g_signal_connect (slave->priv->session,
+        g_signal_connect (slave_session->session,
                           "default-session-name-changed",
                           G_CALLBACK (on_default_session_name_changed),
-                          slave);
+                          slave_session);
 
-        start_autologin_conversation_if_necessary (slave);
+        start_autologin_conversation_if_necessary (slave_session);
 }
 
 static void
-destroy_session (GdmSimpleSlave *slave)
+destroy_session (GdmSlaveSession *slave_session)
 {
-        if (slave->priv->session == NULL) {
+        if (slave_session->session == NULL) {
                 return;
         }
 
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_conversation_started),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_conversation_stopped),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_service_unavailable),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_setup_complete),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_setup_failed),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_authenticated),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_authentication_failed),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_authorized),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_authorization_failed),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_accredited),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_accreditation_failed),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_opened),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_open_failed),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_info),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_problem),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_info_query),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_secret_info_query),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_started),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_exited),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_died),
-                                              slave);
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+                                              slave_session);
+
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_session_selected_user_changed),
-                                              slave);
+                                              slave_session);
 
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_default_language_name_changed),
-                                              slave);
+                                              slave_session);
 
-        g_signal_handlers_disconnect_by_func (slave->priv->session,
+        g_signal_handlers_disconnect_by_func (slave_session->session,
                                               G_CALLBACK (on_default_session_name_changed),
-                                              slave);
+                                              slave_session);
 
-        gdm_session_close (slave->priv->session);
-        g_clear_object (&slave->priv->session);
+        gdm_session_close (slave_session->session);
+        g_clear_object (&slave_session->session);
 }
 
 static void
@@ -941,8 +1019,8 @@ on_greeter_session_stop (GdmGreeterSession *greeter,
         if (slave->priv->start_session_service_name == NULL) {
                 gdm_slave_stopped (GDM_SLAVE (slave));
         } else {
-                gdm_greeter_server_stop (slave->priv->greeter_server);
-                start_session (slave);
+                gdm_greeter_server_stop (slave->priv->primary_session->greeter_server);
+                start_session (slave->priv->primary_session);
         }
 
         g_object_unref (slave->priv->greeter);
@@ -975,115 +1053,116 @@ on_greeter_session_died (GdmGreeterSession    *greeter,
 static void
 on_greeter_start_conversation (GdmGreeterServer *greeter_server,
                                const char       *service_name,
-                               GdmSimpleSlave   *slave)
+                               GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
 
-        g_debug ("GdmSimpleSlave: starting conversation with '%s' pam service'", service_name);
-        gdm_session_start_conversation (slave->priv->session, service_name);
+        g_debug ("GdmSimpleSlave: greeter is starting conversation"
+                 " with '%s' pam service'", service_name);
+        gdm_session_start_conversation (slave_session->session, service_name);
 }
 
 static void
 on_greeter_begin_verification (GdmGreeterServer *greeter_server,
                                const char       *service_name,
-                               GdmSimpleSlave   *slave)
+                               GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: begin verification");
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
-        gdm_session_setup (slave->priv->session, service_name);
+        gdm_session_setup (slave_session->session, service_name);
 }
 
 static void
 on_greeter_begin_auto_login (GdmGreeterServer *greeter_server,
                              const char       *username,
-                             GdmSimpleSlave   *slave)
+                             GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: begin auto login for user '%s'", username);
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
-        gdm_session_setup_for_user (slave->priv->session, "gdm-autologin", username);
+        gdm_session_setup_for_user (slave_session->session, "gdm-autologin", username);
 }
 
 static void
 on_greeter_begin_verification_for_user (GdmGreeterServer *greeter_server,
                                         const char       *service_name,
                                         const char       *username,
-                                        GdmSimpleSlave   *slave)
+                                        GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: begin verification");
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
-        gdm_session_setup_for_user (slave->priv->session, service_name, username);
+        gdm_session_setup_for_user (slave_session->session, service_name, username);
 }
 
 static void
 on_greeter_answer (GdmGreeterServer *greeter_server,
                    const char       *service_name,
                    const char       *text,
-                   GdmSimpleSlave   *slave)
+                   GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
-        gdm_session_answer_query (slave->priv->session, service_name, text);
+        gdm_session_answer_query (slave_session->session, service_name, text);
 }
 
 static void
 on_greeter_session_selected (GdmGreeterServer *greeter_server,
                              const char       *text,
-                             GdmSimpleSlave   *slave)
+                             GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
-        gdm_session_select_session (slave->priv->session, text);
+        gdm_session_select_session (slave_session->session, text);
 }
 
 static void
 on_greeter_language_selected (GdmGreeterServer *greeter_server,
                               const char       *text,
-                              GdmSimpleSlave   *slave)
+                              GdmSlaveSession  *slave_session)
 {
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
-        gdm_session_select_language (slave->priv->session, text);
+        gdm_session_select_language (slave_session->session, text);
 }
 
 static void
 on_greeter_user_selected (GdmGreeterServer *greeter_server,
                           const char       *text,
-                          GdmSimpleSlave   *slave)
+                          GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Greeter user selected");
 }
 
 static void
 on_greeter_cancel (GdmGreeterServer *greeter_server,
-                   GdmSimpleSlave   *slave)
+                   GdmSlaveSession  *slave_session)
 {
         g_debug ("GdmSimpleSlave: Greeter cancelled");
-        queue_greeter_reset (slave);
+        queue_greeter_reset (slave_session);
 }
 
 static void
 on_greeter_connected (GdmGreeterServer *greeter_server,
-                      GdmSimpleSlave   *slave)
+                      GdmSlaveSession  *slave_session)
 {
         gboolean display_is_local;
 
         g_debug ("GdmSimpleSlave: Greeter connected");
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
 
-        g_object_get (slave,
+        g_object_get (slave_session->slave,
                       "display-is-local", &display_is_local,
                       NULL);
 
@@ -1094,29 +1173,48 @@ on_greeter_connected (GdmGreeterServer *greeter_server,
 }
 
 static void
-on_greeter_disconnected (GdmGreeterServer *greeter_server,
-                         GdmSimpleSlave   *slave)
+on_greeter_disconnected_greeter (GdmGreeterServer *greeter_server,
+                                 GdmSlaveSession  *slave_session)
 {
         gboolean display_is_local;
 
-        g_debug ("GdmSimpleSlave: Greeter disconnected");
+        g_debug ("GdmSimpleSlave: Our greeter disconnected");
 
-        g_object_get (slave,
+        g_object_get (slave_session->slave,
                       "display-is-local", &display_is_local,
                       NULL);
 
         if ( ! display_is_local) {
-                gdm_slave_stopped (GDM_SLAVE (slave));
+                gdm_slave_stopped (GDM_SLAVE (slave_session->slave));
         }
 }
 
 static void
+on_greeter_disconnected_external (GdmGreeterServer *greeter_server,
+                                  GdmSlaveSession  *slave_session)
+{
+        GdmSimpleSlave *slave;
+
+        g_debug ("GdmSimpleSlave: External greeter disconnected");
+
+        slave = slave_session->slave;
+
+        slave->priv->other_sessions = g_list_remove (slave->priv->other_sessions,
+                                                     slave_session);
+        free_slave_session (slave_session);
+}
+
+static void
 on_start_session_when_ready (GdmGreeterServer *session,
                              const char       *service_name,
-                             GdmSimpleSlave   *slave)
+                             GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave;
+
+        slave = slave_session->slave;
+
         g_debug ("GdmSimpleSlave: Will start session when ready");
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
         slave->priv->start_session_when_ready = TRUE;
@@ -1129,10 +1227,14 @@ on_start_session_when_ready (GdmGreeterServer *session,
 static void
 on_start_session_later (GdmGreeterServer *session,
                         const char       *service_name,
-                        GdmSimpleSlave   *slave)
+                        GdmSlaveSession  *slave_session)
 {
+        GdmSimpleSlave *slave;
+
+        slave = slave_session->slave;
+
         g_debug ("GdmSimpleSlave: Will start session when ready and told");
-        if (slave->priv->greeter_reset_id > 0) {
+        if (slave_session->greeter_reset_id > 0) {
                 return;
         }
         slave->priv->start_session_when_ready = FALSE;
@@ -1234,6 +1336,77 @@ setup_server (GdmSimpleSlave *slave)
 }
 
 static void
+create_greeter_server (GdmSlaveSession *slave_session,
+                       const char      *display_id,
+                       gboolean         external)
+{
+        slave_session->greeter_server = gdm_greeter_server_new (display_id);
+        g_signal_connect (slave_session->greeter_server,
+                          "start-conversation",
+                          G_CALLBACK (on_greeter_start_conversation),
+                          slave_session);
+        g_signal_connect (slave_session->greeter_server,
+                          "begin-verification",
+                          G_CALLBACK (on_greeter_begin_verification),
+                          slave_session);
+        g_signal_connect (slave_session->greeter_server,
+                          "begin-verification-for-user",
+                          G_CALLBACK (on_greeter_begin_verification_for_user),
+                          slave_session);
+        g_signal_connect (slave_session->greeter_server,
+                          "query-answer",
+                          G_CALLBACK (on_greeter_answer),
+                          slave_session);
+        g_signal_connect (slave_session->greeter_server,
+                          "cancelled",
+                          G_CALLBACK (on_greeter_cancel),
+                          slave_session);
+
+        if (!external) {
+                g_signal_connect (slave_session->greeter_server,
+                                  "begin-auto-login",
+                                  G_CALLBACK (on_greeter_begin_auto_login),
+                                  slave_session);
+                g_signal_connect (slave_session->greeter_server,
+                                  "session-selected",
+                                  G_CALLBACK (on_greeter_session_selected),
+                                  slave_session);
+                g_signal_connect (slave_session->greeter_server,
+                                  "language-selected",
+                                  G_CALLBACK (on_greeter_language_selected),
+                                  slave_session);
+                g_signal_connect (slave_session->greeter_server,
+                                  "user-selected",
+                                  G_CALLBACK (on_greeter_user_selected),
+                                  slave_session);
+                g_signal_connect (slave_session->greeter_server,
+                                  "start-session-when-ready",
+                                  G_CALLBACK (on_start_session_when_ready),
+                                  slave_session);
+                g_signal_connect (slave_session->greeter_server,
+                                  "start-session-later",
+                                  G_CALLBACK (on_start_session_later),
+                                  slave_session);
+
+                g_signal_connect (slave_session->greeter_server,
+                                  "connected",
+                                  G_CALLBACK (on_greeter_connected),
+                                  slave_session);
+                g_signal_connect (slave_session->greeter_server,
+                                  "disconnected",
+                                  G_CALLBACK (on_greeter_disconnected_greeter),
+                                  slave_session);
+        } else {
+                g_signal_connect (slave_session->greeter_server,
+                                  "disconnected",
+                                  G_CALLBACK (on_greeter_disconnected_external),
+                                  slave_session);
+        }
+
+        gdm_greeter_server_start (slave_session->greeter_server, external);
+}
+
+static void
 start_greeter (GdmSimpleSlave *slave)
 {
         gboolean       display_is_local;
@@ -1285,62 +1458,8 @@ start_greeter (GdmSimpleSlave *slave)
         /* Run the init script. gdmslave suspends until script has terminated */
         gdm_slave_run_script (GDM_SLAVE (slave), GDMCONFDIR "/Init", GDM_USERNAME);
 
-        slave->priv->greeter_server = gdm_greeter_server_new (display_id);
-        g_signal_connect (slave->priv->greeter_server,
-                          "start-conversation",
-                          G_CALLBACK (on_greeter_start_conversation),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "begin-auto-login",
-                          G_CALLBACK (on_greeter_begin_auto_login),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "begin-verification",
-                          G_CALLBACK (on_greeter_begin_verification),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "begin-verification-for-user",
-                          G_CALLBACK (on_greeter_begin_verification_for_user),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "query-answer",
-                          G_CALLBACK (on_greeter_answer),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "session-selected",
-                          G_CALLBACK (on_greeter_session_selected),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "language-selected",
-                          G_CALLBACK (on_greeter_language_selected),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "user-selected",
-                          G_CALLBACK (on_greeter_user_selected),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "connected",
-                          G_CALLBACK (on_greeter_connected),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "disconnected",
-                          G_CALLBACK (on_greeter_disconnected),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "cancelled",
-                          G_CALLBACK (on_greeter_cancel),
-                          slave);
-        g_signal_connect (slave->priv->greeter_server,
-                          "start-session-when-ready",
-                          G_CALLBACK (on_start_session_when_ready),
-                          slave);
-
-        g_signal_connect (slave->priv->greeter_server,
-                          "start-session-later",
-                          G_CALLBACK (on_start_session_later),
-                          slave);
-
-        gdm_greeter_server_start (slave->priv->greeter_server);
+        create_greeter_server (slave->priv->primary_session,
+                               display_id, FALSE);
 
         g_debug ("GdmSimpleSlave: Creating greeter on %s %s %s", display_name, display_device, display_hostname);
         slave->priv->greeter = gdm_greeter_session_new (display_name,
@@ -1368,7 +1487,7 @@ start_greeter (GdmSimpleSlave *slave)
                       "x11-authority-file", auth_file,
                       NULL);
 
-        address = gdm_greeter_server_get_address (slave->priv->greeter_server);
+        address = gdm_greeter_server_get_address (slave->priv->primary_session->greeter_server);
         gdm_welcome_session_set_server_address (GDM_WELCOME_SESSION (slave->priv->greeter), address);
         g_free (address);
         gdm_welcome_session_start (GDM_WELCOME_SESSION (slave->priv->greeter));
@@ -1402,11 +1521,11 @@ idle_connect_to_display (GdmSimpleSlave *slave)
                 gdm_slave_get_timed_login_details (GDM_SLAVE (slave), &enabled, NULL, &delay);
                 if (! enabled || delay > 0) {
                         start_greeter (slave);
-                        create_new_session (slave);
+                        create_new_session (slave->priv->primary_session);
                 } else {
                         /* Run the init script. gdmslave suspends until script has terminated */
                         gdm_slave_run_script (GDM_SLAVE (slave), GDMCONFDIR "/Init", GDM_USERNAME);
-                        reset_session (slave);
+                        reset_session (slave->priv->primary_session);
                 }
         } else {
                 if (slave->priv->connection_attempts >= MAX_CONNECT_ATTEMPTS) {
@@ -1544,6 +1663,42 @@ gdm_simple_slave_run (GdmSimpleSlave *slave)
 }
 
 static gboolean
+gdm_simple_slave_get_private_connection (GdmSlave    *slave,
+                                         const char  *session_id,
+                                         char       **address,
+                                         GError     **error)
+{
+        GdmSimpleSlave *self = GDM_SIMPLE_SLAVE (slave);
+        char *actual_session_id;
+        char *display_id;
+        GdmSlaveSession *slave_session;
+
+        actual_session_id = gdm_session_get_session_id (self->priv->primary_session->session);
+        if (actual_session_id && strcmp(actual_session_id, session_id) != 0) {
+                g_set_error (error, GDM_SLAVE_ERROR, GDM_SLAVE_ERROR_WRONG_SESSION,
+                             "Session ID of the caller does not match the one currently supervised"
+                             "by this slave");
+
+                g_free (actual_session_id);
+                return FALSE;
+        }
+        g_free (actual_session_id);
+
+        g_object_get (self, "display-id", &display_id, NULL);
+
+        slave_session = g_slice_new0 (GdmSlaveSession);
+        slave_session->slave = self;
+
+        create_greeter_server (slave_session, display_id, TRUE);
+        create_new_session (slave_session);
+
+        *address = gdm_greeter_server_get_address (slave_session->greeter_server);
+
+        g_free (display_id);
+        return TRUE;
+}
+
+static gboolean
 gdm_simple_slave_start (GdmSlave *slave)
 {
         GDM_SLAVE_CLASS (gdm_simple_slave_parent_class)->start (slave);
@@ -1566,13 +1721,16 @@ gdm_simple_slave_stop (GdmSlave *slave)
                 stop_greeter (self);
         }
 
-        if (self->priv->session != NULL) {
+        g_list_free_full (self->priv->other_sessions, (GDestroyNotify) free_slave_session);
+        self->priv->other_sessions = NULL;
+
+        if (self->priv->primary_session->session != NULL) {
                 char *username;
 
                 /* Run the PostSession script. gdmslave suspends until script
                  * has terminated
                  */
-                username = gdm_session_get_username (self->priv->session);
+                username = gdm_session_get_username (self->priv->primary_session->session);
                 if (username != NULL) {
                         gdm_slave_run_script (slave, GDMCONFDIR "/PostSession", username);
                 }
@@ -1582,8 +1740,8 @@ gdm_simple_slave_stop (GdmSlave *slave)
                 gdm_simple_slave_revoke_console_permissions (self);
 #endif
 
-                gdm_session_close (self->priv->session);
-                g_clear_object (&self->priv->session);
+                gdm_session_close (self->priv->primary_session->session);
+                g_clear_object (&self->priv->primary_session->session);
         }
 
         if (self->priv->server != NULL) {
@@ -1647,6 +1805,7 @@ gdm_simple_slave_class_init (GdmSimpleSlaveClass *klass)
 
         slave_class->start = gdm_simple_slave_start;
         slave_class->stop = gdm_simple_slave_stop;
+        slave_class->get_private_connection = gdm_simple_slave_get_private_connection;
 
         g_type_class_add_private (klass, sizeof (GdmSimpleSlavePrivate));
 }
@@ -1658,6 +1817,9 @@ gdm_simple_slave_init (GdmSimpleSlave *slave)
 #ifdef  HAVE_LOGINDEVPERM
         slave->priv->use_logindevperm = FALSE;
 #endif
+
+        slave->priv->primary_session = g_slice_new0 (GdmSlaveSession);
+        slave->priv->primary_session->slave = slave;
 }
 
 static void
@@ -1674,9 +1836,9 @@ gdm_simple_slave_finalize (GObject *object)
 
         gdm_simple_slave_stop (GDM_SLAVE (simple_slave));
 
-        if (simple_slave->priv->greeter_reset_id > 0) {
-                g_source_remove (simple_slave->priv->greeter_reset_id);
-                simple_slave->priv->greeter_reset_id = 0;
+        if (simple_slave->priv->primary_session->greeter_reset_id > 0) {
+                g_source_remove (simple_slave->priv->primary_session->greeter_reset_id);
+                simple_slave->priv->primary_session->greeter_reset_id = 0;
         }
 
         G_OBJECT_CLASS (gdm_simple_slave_parent_class)->finalize (object);
diff --git a/daemon/gdm-slave.c b/daemon/gdm-slave.c
index 94c8d44..d8cf9cc 100644
--- a/daemon/gdm-slave.c
+++ b/daemon/gdm-slave.c
@@ -76,6 +76,8 @@
 #define GDM_DBUS_NAME              "org.gnome.DisplayManager"
 #define GDM_DBUS_DISPLAY_INTERFACE "org.gnome.DisplayManager.Display"
 
+#define GDM_SLAVE_PATH "/org/gnome/DisplayManager/Slave"
+
 #define MAX_CONNECT_ATTEMPTS 10
 
 struct GdmSlavePrivate
@@ -102,6 +104,7 @@ struct GdmSlavePrivate
 
         GdmDBusDisplay  *display_proxy;
         GDBusConnection *connection;
+        GdmDBusSlave    *skeleton;
 };
 
 enum {
@@ -130,6 +133,17 @@ G_DEFINE_ABSTRACT_TYPE (GdmSlave, gdm_slave, G_TYPE_OBJECT)
 
 #define CURSOR_WATCH XC_watch
 
+GQuark
+gdm_slave_error_quark (void)
+{
+        static GQuark ret = 0;
+        if (ret == 0) {
+                ret = g_quark_from_static_string ("gdm-slave-error-quark");
+        }
+
+        return ret;
+}
+
 static void
 gdm_slave_whack_temp_auth_file (GdmSlave *slave)
 {
@@ -1820,6 +1834,42 @@ gdm_slave_get_property (GObject    *object,
 }
 
 static gboolean
+handle_get_private_connection (GdmDBusSlave          *skeleton,
+                               GDBusMethodInvocation *invocation,
+                               const char            *session_id,
+                               GdmSlave              *slave)
+{
+        GError *local_error;
+        GdmSlaveClass *klass;
+        char *address;
+
+        klass = GDM_SLAVE_GET_CLASS (slave);
+        if (!klass->get_private_connection) {
+                g_dbus_method_invocation_return_dbus_error (invocation,
+                                                            "org.gnome.DisplayManager.Slave.Unsupported",
+                                                            "Connections to the slave are not supported by this slave");
+                return TRUE;
+        }
+
+        local_error = NULL;
+        address = NULL;
+        if (!klass->get_private_connection (slave,
+                                            session_id,
+                                            &address,
+                                            &local_error)) {
+                g_dbus_method_invocation_return_gerror (invocation,
+                                                        local_error);
+                g_error_free (local_error);
+                return TRUE;
+        }
+
+        gdm_dbus_slave_complete_get_private_connection (skeleton, invocation, address);
+
+        g_free (address);
+        return TRUE;
+}
+
+static gboolean
 register_slave (GdmSlave *slave)
 {
         GError *error;
@@ -1834,6 +1884,16 @@ register_slave (GdmSlave *slave)
                 exit (1);
         }
 
+        slave->priv->skeleton = GDM_DBUS_SLAVE (gdm_dbus_slave_skeleton_new ());
+
+        g_signal_connect (slave->priv->skeleton,
+                          "handle-get-private-connection",
+                          G_CALLBACK (handle_get_private_connection),
+                          slave);
+
+        gdm_slave_export_interface (slave,
+                                    G_DBUS_INTERFACE_SKELETON (slave->priv->skeleton));
+
         return TRUE;
 }
 
@@ -1972,3 +2032,13 @@ gdm_slave_finalize (GObject *object)
 
         G_OBJECT_CLASS (gdm_slave_parent_class)->finalize (object);
 }
+
+void
+gdm_slave_export_interface (GdmSlave               *slave,
+                            GDBusInterfaceSkeleton *interface)
+{
+        g_dbus_interface_skeleton_export (interface,
+                                          slave->priv->connection,
+                                          GDM_SLAVE_PATH,
+                                          NULL);
+}
diff --git a/daemon/gdm-slave.h b/daemon/gdm-slave.h
index 128ca3e..d399646 100644
--- a/daemon/gdm-slave.h
+++ b/daemon/gdm-slave.h
@@ -50,10 +50,26 @@ typedef struct
         gboolean (*start) (GdmSlave *slave);
         gboolean (*stop)  (GdmSlave *slave);
 
+        gboolean (*get_private_connection) (GdmSlave    *slave,
+                                            const char  *session_id,
+                                            char       **address,
+                                            GError     **error);
+
         /* signals */
         void (*stopped) (GdmSlave *slave);
 } GdmSlaveClass;
 
+typedef enum
+{
+        GDM_SLAVE_ERROR_GENERIC,
+        GDM_SLAVE_ERROR_UNSUPPORTED,
+        GDM_SLAVE_ERROR_NOT_OPENED,
+        GDM_SLAVE_ERROR_WRONG_SESSION,
+} GdmSlaveError;
+
+#define GDM_SLAVE_ERROR (gdm_slave_error_quark ())
+
+GQuark              gdm_slave_error_quark            (void);
 GType               gdm_slave_get_type               (void);
 gboolean            gdm_slave_start                  (GdmSlave   *slave);
 gboolean            gdm_slave_stop                   (GdmSlave   *slave);
@@ -85,6 +101,10 @@ gboolean            gdm_slave_run_script             (GdmSlave   *slave,
                                                       const char *username);
 void                gdm_slave_stopped                (GdmSlave   *slave);
 
+void                gdm_slave_export_interface       (GdmSlave               *slave,
+                                                      GDBusInterfaceSkeleton *interface);
+
+
 G_END_DECLS
 
 #endif /* __GDM_SLAVE_H */
diff --git a/daemon/gdm-slave.xml b/daemon/gdm-slave.xml
index 6566594..5bc238b 100644
--- a/daemon/gdm-slave.xml
+++ b/daemon/gdm-slave.xml
@@ -1,5 +1,9 @@
 <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
 <node>
   <interface name="org.gnome.DisplayManager.Slave">
+    <method name="GetPrivateConnection">
+      <arg name="session_id" type="s" direction="in" />
+      <arg name="address" type="s" direction="out" />
+    </method>
   </interface>
 </node>
diff --git a/daemon/test-external-greeter.c b/daemon/test-external-greeter.c
new file mode 100644
index 0000000..a3b6030
--- /dev/null
+++ b/daemon/test-external-greeter.c
@@ -0,0 +1,261 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+
+#include <glib.h>
+
+#include "gdm-display-glue.h"
+#include "gdm-greeter-glue.h"
+
+static GMainLoop *loop;
+
+static void
+on_ready (GdmDBusGreeterServer *greeter,
+          const char           *service_name)
+{
+        GError *error;
+        gboolean ok;
+
+        error = NULL;
+        ok = gdm_dbus_greeter_server_call_begin_verification_for_user_sync (greeter,
+                                                                            service_name,
+                                                                            g_get_user_name (),
+                                                                            NULL, &error);
+        if (!ok) {
+                g_critical ("Failed to start PAM session: %s", error->message);
+                exit (1);
+        }
+}
+
+static void
+on_conversation_stopped (GdmDBusGreeterServer *greeter,
+                         const char           *service_name)
+{
+        g_print ("\n** WARNING: conversation stopped\n");
+
+        g_main_loop_quit (loop);
+}
+
+static void
+on_reset (GdmDBusGreeterServer *greeter)
+{
+        g_print ("\n** NOTE: reset\n");
+
+        g_main_loop_quit (loop);
+}
+
+static void
+on_session_opened (GdmDBusGreeterServer *greeter,
+                   const char           *service_name)
+{
+        g_print ("\n** INFO: session opened (authentication OK)\n");
+
+        g_main_loop_quit (loop);
+}
+
+static void
+on_info_query (GdmDBusGreeterServer *greeter,
+               const char           *service_name,
+               const char           *query_text)
+{
+        char  answer[1024];
+        char *res;
+
+        g_print ("%s ", query_text);
+
+        answer[0] = '\0';
+        res = fgets (answer, sizeof (answer), stdin);
+        if (res == NULL) {
+                g_warning ("Couldn't get an answer");
+        }
+
+        answer[strlen (answer) - 1] = '\0';
+
+        if (answer[0] == '\0') {
+                gdm_dbus_greeter_server_call_cancel_sync (greeter,
+                                                   NULL, NULL);
+                g_main_loop_quit (loop);
+        } else {
+                gdm_dbus_greeter_server_call_answer_query_sync (greeter,
+                                                                service_name,
+                                                                answer,
+                                                                NULL, NULL);
+        }
+}
+
+static void
+on_info (GdmDBusGreeterServer *greeter,
+         const char           *service_name,
+         const char           *info)
+{
+        g_print ("\n** NOTE: %s\n", info);
+}
+
+static void
+on_problem (GdmDBusGreeterServer *greeter,
+            const char           *service_name,
+            const char           *problem)
+{
+        g_print ("\n** WARNING: %s\n", problem);
+}
+
+static void
+on_secret_info_query (GdmDBusGreeterServer *greeter,
+                      const char           *service_name,
+                      const char           *query_text)
+{
+        char           answer[1024];
+        char          *res;
+        struct termios ts0;
+        struct termios ts1;
+
+        tcgetattr (fileno (stdin), &ts0);
+        ts1 = ts0;
+        ts1.c_lflag &= ~ECHO;
+
+        g_print ("%s", query_text);
+
+        if (tcsetattr (fileno (stdin), TCSAFLUSH, &ts1) != 0) {
+                fprintf (stderr, "Could not set terminal attributes\n");
+                exit (1);
+        }
+
+        answer[0] = '\0';
+        res = fgets (answer, sizeof (answer), stdin);
+        answer[strlen (answer) - 1] = '\0';
+        if (res == NULL) {
+                g_warning ("Couldn't get an answer");
+        }
+
+        tcsetattr (fileno (stdin), TCSANOW, &ts0);
+
+        g_print ("\n");
+
+        gdm_dbus_greeter_server_call_answer_query_sync (greeter, service_name, answer,
+                                                        NULL, NULL);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+        GError *error;
+        GdmDBusDisplay *display;
+        GdmDBusGreeterServer *greeter;
+        GDBusConnection *system_bus;
+        GDBusConnection *connection;
+        char *address;
+        char *display_name;
+        char *display_id;
+        gboolean ok;
+
+        g_type_init ();
+
+        g_debug ("creating instance of GdmDBusDisplay object...");
+
+        error = NULL;
+        system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+        if (!system_bus) {
+                g_critical ("Failed connecting to the system bus (this is pretty bad): %s", error->message);
+                exit (1);
+        }
+
+        display_name = g_strdelimit (g_strdup (g_getenv ("DISPLAY")),
+                                     ":" G_STR_DELIMITERS, '_');
+        display_id = g_strdup_printf ("/org/gnome/DisplayManager/Displays/%s", display_name);
+
+        display = GDM_DBUS_DISPLAY (gdm_dbus_display_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                                             G_DBUS_PROXY_FLAGS_NONE,
+                                                                             "org.gnome.DisplayManager",
+                                                                             display_id,
+                                                                             NULL, &error));
+        if (!display) {
+                g_critical ("Failed creating display proxy: %s", error->message);
+                exit (1);
+        }
+
+        g_free (display_name);
+        g_free (display_id);
+
+        address = NULL;
+        gdm_dbus_display_call_connect_to_slave_sync (display,
+                                                     &address,
+                                                     NULL, &error);
+        if (!address) {
+                g_critical ("Failed obtaining slave address: %s", error->message);
+                exit (1);
+        }
+
+        connection = g_dbus_connection_new_for_address_sync (address,
+                                                             G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+                                                             NULL,
+                                                             NULL, &error);
+        if (!connection) {
+                g_critical ("Failed connecting to the slave: %s", error->message);
+                exit (1);
+        }
+
+        greeter = GDM_DBUS_GREETER_SERVER (gdm_dbus_greeter_server_proxy_new_sync (connection,
+                                                                                   G_DBUS_PROXY_FLAGS_NONE,
+                                                                                   NULL,
+                                                                                   "/org/gnome/DisplayManager/GreeterServer",
+                                                                                   NULL, &error));
+        if (!greeter) {
+                g_critical ("Failed creating greeter proxy: %s", error->message);
+                exit (1);
+        }
+
+        g_signal_connect (greeter, "ready",
+                          G_CALLBACK (on_ready), NULL);
+        g_signal_connect (greeter, "info",
+                          G_CALLBACK (on_info), NULL);
+        g_signal_connect (greeter, "problem",
+                          G_CALLBACK (on_problem), NULL);
+        g_signal_connect (greeter, "info-query",
+                          G_CALLBACK (on_info_query), NULL);
+        g_signal_connect (greeter, "secret-info-query",
+                          G_CALLBACK (on_secret_info_query), NULL);
+        g_signal_connect (greeter, "conversation-stopped",
+                          G_CALLBACK (on_conversation_stopped), NULL);
+        g_signal_connect (greeter, "session-opened",
+                          G_CALLBACK (on_session_opened), NULL);
+        g_signal_connect (greeter, "reset",
+                          G_CALLBACK (on_reset), NULL);
+
+        ok = gdm_dbus_greeter_server_call_start_conversation_sync (greeter,
+                                                                   "gdm-password",
+                                                                   NULL, &error);
+        if (!ok) {
+                g_critical ("Failed to start conversation: %s", error->message);
+                exit (1);
+        }
+
+        loop = g_main_loop_new (NULL, FALSE);
+        g_main_loop_run (loop);
+
+        return 0;
+}
diff --git a/daemon/test-session.c b/daemon/test-session.c
index 31be1ec..c8e2151 100644
--- a/daemon/test-session.c
+++ b/daemon/test-session.c
@@ -246,7 +246,8 @@ main (int   argc,
         g_type_init ();
 
         do {
-                session = gdm_session_new ("/org/gnome/DisplayManager/Displays/1",
+                g_debug ("creating instance of GdmSession object...");
+                session = gdm_session_new ("/org/gnome/DisplayManager/Displays/_0",
                                            ":0",
                                            g_get_host_name (),
                                            ttyname (STDIN_FILENO),
diff --git a/data/gdm.conf.in b/data/gdm.conf.in
index 23e5fcc..1bd6fc7 100644
--- a/data/gdm.conf.in
+++ b/data/gdm.conf.in
@@ -24,6 +24,11 @@
     <allow send_destination="org.gnome.DisplayManager"
            send_interface="org.freedesktop.DBus.Introspectable"/>
 
+    <!-- root can request private connections to everyone
+         (as long as they implement the interface, that is) -->
+    <allow send_interface="org.gnome.DisplayManager.Slave"
+	   send_member="GetPrivateConnection"/>
+
   </policy>
 
   <policy context="default">
@@ -43,6 +48,8 @@
           send_interface="org.freedesktop.DBus.Properties" />
     <allow send_destination="org.gnome.DisplayManager"
            send_interface="org.freedesktop.DBus.Introspectable"/>
+    <allow send_destination="org.gnome.DisplayManager"
+	   send_interface="org.freedesktop.DBus.ObjectManager"/>
 
     <allow send_destination="org.gnome.DisplayManager"
            send_interface="org.gnome.DisplayManager.Display"
@@ -62,15 +69,13 @@
     <allow send_destination="org.gnome.DisplayManager"
            send_interface="org.gnome.DisplayManager.Display"
            send_member="IsLocal"/>
+    <allow send_destination="org.gnome.DisplayManager"
+	   send_interface="org.gnome.DisplayManager.Display"
+	   send_member="ConnectToSlave"/>
 
     <allow send_destination="org.gnome.DisplayManager"
            send_interface="org.gnome.DisplayManager.LocalDisplayFactory"
            send_member="CreateTransientDisplay"/>
-
-    <allow send_destination="org.gnome.DisplayManager"
-           send_interface="org.gnome.DisplayManager.Manager"
-           send_member="GetDisplays"/>
-
   </policy>
 
   <policy user="@GDM_USERNAME@">



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]