]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blobdiff - liblightdm-gobject/greeter.c
Don't access unreffed value
[sojka/lightdm.git] / liblightdm-gobject / greeter.c
index 8f90308fe4ceb24710c7e265036f021933ce2834..5e6996d3e0eeea02cac78009889eeaaa07637620 100644 (file)
@@ -39,22 +39,46 @@ enum {
     SHOW_MESSAGE,
     AUTHENTICATION_COMPLETE,
     AUTOLOGIN_TIMER_EXPIRED,
+    IDLE,
+    RESET,
     LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct
 {
-    gboolean connected;
+    /* TRUE if the daemon can reuse this greeter */
+    gboolean resettable;
+
+    /* Channel to write to daemon */
+    GIOChannel *to_server_channel;
 
-    GIOChannel *to_server_channel, *from_server_channel;
+    /* Channel to read from daemon */
+    GIOChannel *from_server_channel;
+
+    /* Data read from the daemon */
     guint8 *read_buffer;
     gsize n_read;
 
     gsize n_responses_waiting;
     GList *responses_received;
 
+    /* TRUE if have got a connect response */
+    gboolean connected;
+
+    /* Pending connect requests */
+    GList *connect_requests;
+
+    /* Pending start session requests */
+    GList *start_session_requests;
+
+    /* Pending ensure shared data dir requests */
+    GList *ensure_shared_data_dir_requests;
+
+    /* Hints provided by the daemon */
     GHashTable *hints;
+
+    /* Timeout source to notify greeter to autologin */
     guint autologin_timeout;
 
     gchar *authentication_user;
@@ -81,7 +105,8 @@ typedef enum
     GREETER_MESSAGE_START_SESSION,
     GREETER_MESSAGE_CANCEL_AUTHENTICATION,
     GREETER_MESSAGE_SET_LANGUAGE,
-    GREETER_MESSAGE_AUTHENTICATE_REMOTE
+    GREETER_MESSAGE_AUTHENTICATE_REMOTE,
+    GREETER_MESSAGE_ENSURE_SHARED_DIR,
 } GreeterMessage;
 
 /* Messages from the server to the greeter */
@@ -90,9 +115,67 @@ typedef enum
     SERVER_MESSAGE_CONNECTED = 0,
     SERVER_MESSAGE_PROMPT_AUTHENTICATION,
     SERVER_MESSAGE_END_AUTHENTICATION,
-    SERVER_MESSAGE_SESSION_RESULT
+    SERVER_MESSAGE_SESSION_RESULT,
+    SERVER_MESSAGE_SHARED_DIR_RESULT,
+    SERVER_MESSAGE_IDLE,
+    SERVER_MESSAGE_RESET,
 } ServerMessage;
 
+/* Request sent to server */
+typedef struct
+{
+    GObject parent_instance;
+    GCancellable *cancellable;
+    GAsyncReadyCallback callback;
+    gpointer user_data;
+    gboolean complete;
+    guint32 return_code;
+    gchar *dir;
+} Request;
+typedef struct
+{
+    GObjectClass parent_class;
+} RequestClass;
+GType request_get_type (void);
+static void request_iface_init (GAsyncResultIface *iface);
+#define REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), request_get_type (), Request))
+G_DEFINE_TYPE_WITH_CODE (Request, request, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, request_iface_init));
+
+GType
+lightdm_prompt_type_get_type (void)
+{
+    static GType enum_type = 0;
+  
+    if (G_UNLIKELY(enum_type == 0)) {
+        static const GEnumValue values[] = {
+            { LIGHTDM_PROMPT_TYPE_QUESTION, "LIGHTDM_PROMPT_TYPE_QUESTION", "question" },
+            { LIGHTDM_PROMPT_TYPE_SECRET, "LIGHTDM_PROMPT_TYPE_SECRET", "secret" },
+            { 0, NULL, NULL }
+        };
+        enum_type = g_enum_register_static (g_intern_static_string ("LightDMPromptType"), values);
+    }
+
+    return enum_type;
+}
+
+GType
+lightdm_message_type_get_type (void)
+{
+    static GType enum_type = 0;
+  
+    if (G_UNLIKELY(enum_type == 0)) {
+        static const GEnumValue values[] = {
+            { LIGHTDM_MESSAGE_TYPE_INFO, "LIGHTDM_MESSAGE_TYPE_INFO", "info" },
+            { LIGHTDM_MESSAGE_TYPE_ERROR, "LIGHTDM_MESSAGE_TYPE_ERROR", "error" },
+            { 0, NULL, NULL }
+        };
+        enum_type = g_enum_register_static (g_intern_static_string ("LightDMMessageType"), values);
+    }
+
+    return enum_type;
+}
+
+
 /**
  * lightdm_greeter_new:
  *
@@ -101,11 +184,60 @@ typedef enum
  * Return value: the new #LightDMGreeter
  **/
 LightDMGreeter *
-lightdm_greeter_new ()
+lightdm_greeter_new (void)
 {
     return g_object_new (LIGHTDM_TYPE_GREETER, NULL);
 }
 
+/**
+ * lightdm_greeter_set_resettable:
+ * @greeter: A #LightDMGreeter
+ * @resettable: Whether the greeter wants to be reset instead of killed after the user logs in
+ *
+ * Set whether the greeter will be reset instead of killed after the user logs in.
+ * This must be called before lightdm_greeter_connect is called.
+ **/
+void
+lightdm_greeter_set_resettable (LightDMGreeter *greeter, gboolean resettable)
+{
+    LightDMGreeterPrivate *priv;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_if_fail (!priv->connected);
+    priv->resettable = resettable;
+}
+
+static Request *
+request_new (GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+    Request *request;
+
+    request = g_object_new (request_get_type (), NULL);
+    if (cancellable)
+        request->cancellable = g_object_ref (cancellable);
+    request->callback = callback;
+    request->user_data = user_data;
+
+    return request;
+}
+
+static void
+request_complete (Request *request, GObject *object)
+{
+    request->complete = TRUE;
+
+    if (!request->callback)
+        return;
+
+    if (request->cancellable && g_cancellable_is_cancelled (request->cancellable))
+        return;
+
+    request->callback (object, G_ASYNC_RESULT (request), request->user_data);
+}
+
 static gboolean
 timed_login_cb (gpointer data)
 {
@@ -146,7 +278,8 @@ write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *off
     write_int (buffer, buffer_length, length, offset);
     if (*offset + length >= buffer_length)
         return;
-    memcpy (buffer + *offset, value, length);
+    if (value)
+        memcpy (buffer + *offset, value, length);
     *offset += length;
 }
 
@@ -213,14 +346,18 @@ get_message_length (guint8 *message, gsize message_length)
     return read_int (message, message_length, &offset);
 }
 
-static void
-write_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
+static gboolean
+send_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
 {
     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
-    GIOStatus status;
+    gchar *data;
+    gsize data_length;
     GError *error = NULL;
     guint32 stated_length;
 
+    if (!priv->to_server_channel)
+        return FALSE;
+
     /* Double check that we're sending well-formed messages.  If we say we're
        sending more than we do, we end up DOS'ing lightdm as it waits for the
        rest.  If we say we're sending less than we do, we confuse the heck out
@@ -230,16 +367,33 @@ write_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
     if (stated_length != message_length)
     {
         g_warning ("Refusing to write malformed packet to daemon: declared size is %u, but actual size is %zu", stated_length, message_length);
-        return;
+        return FALSE;
+    }
+
+    data = (gchar *) message;
+    data_length = message_length;
+    while (data_length > 0)
+    {
+        GIOStatus status;
+        gsize n_written;
+
+        status = g_io_channel_write_chars (priv->to_server_channel, data, data_length, &n_written, &error);
+        if (error)
+            g_warning ("Error writing to daemon: %s", error->message);
+        g_clear_error (&error);
+        if (status != G_IO_STATUS_NORMAL)
+            return FALSE;
+        data_length -= n_written;
+        data += n_written;
     }
 
-    status = g_io_channel_write_chars (priv->to_server_channel, (gchar *) message, message_length, NULL, &error);
+    g_debug ("Wrote %zi bytes to daemon", message_length);
+    g_io_channel_flush (priv->to_server_channel, &error);
     if (error)
-        g_warning ("Error writing to daemon: %s", error->message);
+        g_warning ("Failed to flush data to daemon: %s", error->message);
     g_clear_error (&error);
-    if (status == G_IO_STATUS_NORMAL)
-        g_debug ("Wrote %zi bytes to daemon", message_length);
-    g_io_channel_flush (priv->to_server_channel, NULL);
+
+    return TRUE;
 }
 
 static void
@@ -249,19 +403,21 @@ handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length
     gchar *version;
     GString *hint_string;
     int timeout;
+    Request *request;
 
     version = read_string (message, message_length, offset);
     hint_string = g_string_new ("");
     while (*offset < message_length)
     {
         gchar *name, *value;
-      
+
         name = read_string (message, message_length, offset);
         value = read_string (message, message_length, offset);
         g_hash_table_insert (priv->hints, name, value);
         g_string_append_printf (hint_string, " %s=%s", name, value);
     }
 
+    priv->connected = TRUE;
     g_debug ("Connected version=%s%s", version, hint_string->str);
     g_free (version);
     g_string_free (hint_string, TRUE);
@@ -273,6 +429,15 @@ handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length
         g_debug ("Setting autologin timer for %d seconds", timeout);
         priv->autologin_timeout = g_timeout_add (timeout * 1000, timed_login_cb, greeter);
     }
+
+    /* Notify asynchronous caller */
+    request = g_list_nth_data (priv->connect_requests, 0);
+    if (request)
+    {
+        request_complete (request, G_OBJECT (greeter));
+        priv->connect_requests = g_list_remove (priv->connect_requests, request);
+        g_object_unref (request);
+    }
 }
 
 static void
@@ -379,14 +544,125 @@ handle_end_authentication (LightDMGreeter *greeter, guint8 *message, gsize messa
     g_signal_emit (G_OBJECT (greeter), signals[AUTHENTICATION_COMPLETE], 0);
 }
 
+static void
+handle_idle (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+    g_signal_emit (G_OBJECT (greeter), signals[IDLE], 0);
+}
+
+static void
+handle_reset (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    GString *hint_string;
+
+    g_hash_table_remove_all (priv->hints);
+
+    hint_string = g_string_new ("");
+    while (*offset < message_length)
+    {
+        gchar *name, *value;
+
+        name = read_string (message, message_length, offset);
+        value = read_string (message, message_length, offset);
+        g_hash_table_insert (priv->hints, name, value);
+        g_string_append_printf (hint_string, " %s=%s", name, value);
+    }
+
+    g_debug ("Reset%s", hint_string->str);
+    g_string_free (hint_string, TRUE);
+
+    g_signal_emit (G_OBJECT (greeter), signals[RESET], 0);
+}
+
+static void
+handle_session_result (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    Request *request;
+
+    /* Notify asynchronous caller */
+    request = g_list_nth_data (priv->start_session_requests, 0);
+    if (request)
+    {
+        request->return_code = read_int (message, message_length, offset);
+        request_complete (request, G_OBJECT (greeter));
+        priv->start_session_requests = g_list_remove (priv->start_session_requests, request);
+        g_object_unref (request);
+    }
+}
+
+static void
+handle_shared_dir_result (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    Request *request;
+
+    /* Notify asynchronous caller */
+    request = g_list_nth_data (priv->ensure_shared_data_dir_requests, 0);
+    if (request)
+    {
+        request->dir = read_string (message, message_length, offset);
+        /* Blank data dir means invalid user */
+        if (g_strcmp0 (request->dir, "") == 0)
+        {
+            g_free (request->dir);
+            request->dir = NULL;
+        }
+        request_complete (request, G_OBJECT (greeter));
+        priv->ensure_shared_data_dir_requests = g_list_remove (priv->ensure_shared_data_dir_requests, request);
+        g_object_unref (request);
+    }
+}
+
+static void
+handle_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
+{
+    gsize offset = 0;
+    guint32 id;
+
+    id = read_int (message, message_length, &offset);
+    read_int (message, message_length, &offset);
+    switch (id)
+    {
+    case SERVER_MESSAGE_CONNECTED:
+        handle_connected (greeter, message, message_length, &offset);
+        break;
+    case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
+        handle_prompt_authentication (greeter, message, message_length, &offset);
+        break;
+    case SERVER_MESSAGE_END_AUTHENTICATION:
+        handle_end_authentication (greeter, message, message_length, &offset);
+        break;
+    case SERVER_MESSAGE_SESSION_RESULT:
+        handle_session_result (greeter, message, message_length, &offset);
+        break;
+    case SERVER_MESSAGE_SHARED_DIR_RESULT:
+        handle_shared_dir_result (greeter, message, message_length, &offset);
+        break;
+    case SERVER_MESSAGE_IDLE:
+        handle_idle (greeter, message, message_length, &offset);
+        break;
+    case SERVER_MESSAGE_RESET:
+        handle_reset (greeter, message, message_length, &offset);
+        break;
+    default:
+        g_warning ("Unknown message from server: %d", id);
+        break;
+    }
+}
+
 static guint8 *
-read_message (LightDMGreeter *greeter, gsize *length, gboolean block)
+recv_message (LightDMGreeter *greeter, gsize *length, gboolean block)
 {
     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
     gsize n_to_read, n_read;
     guint8 *buffer;
     GError *error = NULL;
 
+    if (!priv->from_server_channel)
+        return NULL;
+
     /* Read the header, or the whole message if we already have that */
     n_to_read = HEADER_SIZE;
     if (priv->n_read >= HEADER_SIZE)
@@ -413,7 +689,7 @@ read_message (LightDMGreeter *greeter, gsize *length, gboolean block)
 
     /* Stop if haven't got all the data we want */
     if (priv->n_read != n_to_read)
-        return FALSE;
+        return NULL;
 
     /* If have header, rerun for content */
     if (priv->n_read == HEADER_SIZE)
@@ -422,7 +698,7 @@ read_message (LightDMGreeter *greeter, gsize *length, gboolean block)
         if (n_to_read > 0)
         {
             priv->read_buffer = g_realloc (priv->read_buffer, HEADER_SIZE + n_to_read);
-            return read_message (greeter, length, block);
+            return recv_message (greeter, length, block);
         }
     }
 
@@ -440,35 +716,109 @@ from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
 {
     LightDMGreeter *greeter = data;
     guint8 *message;
-    gsize message_length, offset;
-    guint32 id;
+    gsize message_length;
 
-    message = read_message (greeter, &message_length, FALSE);
-    if (!message)
-        return TRUE;
-
-    offset = 0;
-    id = read_int (message, message_length, &offset);
-    read_int (message, message_length, &offset);
-    switch (id)
+    /* Read one message and process it */
+    message = recv_message (greeter, &message_length, FALSE);
+    if (message)
     {
-    case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
-        handle_prompt_authentication (greeter, message, message_length, &offset);
-        break;
-    case SERVER_MESSAGE_END_AUTHENTICATION:
-        handle_end_authentication (greeter, message, message_length, &offset);
-        break;
-    default:
-        g_warning ("Unknown message from server: %d", id);
-        break;
+        handle_message (greeter, message, message_length);
+        g_free (message);
     }
-    g_free (message);
 
     return TRUE;
 }
 
+static gboolean
+send_connect (LightDMGreeter *greeter, gboolean resettable)
+{
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_debug ("Connecting to display manager...");
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION) + int_length (), &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
+    write_int (message, MAX_MESSAGE_LENGTH, resettable ? 1 : 0, &offset);
+
+    return send_message (greeter, message, offset);
+}
+
+static gboolean
+send_start_session (LightDMGreeter *greeter, const gchar *session)
+{
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    if (session)
+        g_debug ("Starting session %s", session);
+    else
+        g_debug ("Starting default session");
+
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_START_SESSION, string_length (session), &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
+    return send_message (greeter, message, offset);
+}
+
+static gboolean
+send_ensure_shared_data_dir (LightDMGreeter *greeter, const gchar *username)
+{
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_debug ("Ensuring data directory for user %s", username);
+
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_ENSURE_SHARED_DIR, string_length (username), &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
+    return send_message (greeter, message, offset);
+}
+
 /**
- * lightdm_greeter_connect_sync:
+ * lightdm_greeter_connect_to_daemon:
+ * @greeter: The greeter to connect
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: (allow-none): A #GAsyncReadyCallback to call when completed or %NULL.
+ * @user_data: (allow-none): data to pass to the @callback or %NULL.
+ *
+ * Asynchronously connects the greeter to the display manager.
+ *
+ * When the operation is finished, @callback will be invoked. You can then call lightdm_greeter_connect_to_daemon_finish() to get the result of the operation.
+ *
+ * See lightdm_greeter_connect_to_daemon_sync() for the synchronous version.
+ **/
+void
+lightdm_greeter_connect_to_daemon (LightDMGreeter *greeter, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+    LightDMGreeterPrivate *priv;
+    Request *request;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    request = request_new (cancellable, callback, user_data);
+    priv->connect_requests = g_list_append (priv->connect_requests, request);
+    send_connect (greeter, priv->resettable);
+}
+
+/**
+ * lightdm_greeter_connect_to_daemon_finish:
+ * @greeter: The greeter the the request was done with
+ * @result: A #GAsyncResult.
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an operation started with lightdm_greeter_connect_to_daemon().
+ *
+ * Return value: #TRUE if successfully connected
+ **/
+gboolean
+lightdm_greeter_connect_to_daemon_finish (LightDMGreeter *greeter, GAsyncResult *result, GError **error)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    return REQUEST (result)->complete;
+}
+
+/**
+ * lightdm_greeter_connect_to_daemon_sync:
  * @greeter: The greeter to connect
  * @error: return location for a #GError, or %NULL
  *
@@ -477,62 +827,53 @@ from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
  * Return value: #TRUE if successfully connected
  **/
 gboolean
-lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error)
+lightdm_greeter_connect_to_daemon_sync (LightDMGreeter *greeter, GError **error)
 {
     LightDMGreeterPrivate *priv;
-    const gchar *fd;
-    guint8 message[MAX_MESSAGE_LENGTH];
-    guint8 *response;
-    gsize response_length, offset = 0;
-    guint32 id;
+    Request *request;
+    gboolean result;
 
     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
 
     priv = GET_PRIVATE (greeter);
 
-    fd = g_getenv ("LIGHTDM_TO_SERVER_FD");
-    if (!fd)
-    {
-        g_warning ("No LIGHTDM_TO_SERVER_FD environment variable");
-        return FALSE;
-    }
-    priv->to_server_channel = g_io_channel_unix_new (atoi (fd));
-    g_io_channel_set_encoding (priv->to_server_channel, NULL, NULL);
-
-    fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
-    if (!fd)
+    /* Read until we are connected */
+    send_connect (greeter, priv->resettable);
+    request = request_new (NULL, NULL, NULL);
+    priv->connect_requests = g_list_append (priv->connect_requests, g_object_ref (request));
+    do
     {
-        g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable");
-        return FALSE;
-    }
-    priv->from_server_channel = g_io_channel_unix_new (atoi (fd));
-    g_io_channel_set_encoding (priv->from_server_channel, NULL, NULL);
-    g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter);
-
-    g_debug ("Connecting to display manager...");
-    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION), &offset);
-    write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
-    write_message (greeter, message, offset);
+        guint8 *message;
+        gsize message_length;
 
-    response = read_message (greeter, &response_length, TRUE);
-    if (!response)
-        return FALSE;
+        message = recv_message (greeter, &message_length, TRUE);
+        if (!message)
+            break;
+        handle_message (greeter, message, message_length);
+        g_free (message);
+    } while (!request->complete);
 
-    offset = 0;
-    id = read_int (response, response_length, &offset);
-    read_int (response, response_length, &offset);
-    if (id == SERVER_MESSAGE_CONNECTED)
-        handle_connected (greeter, response, response_length, &offset);
-    g_free (response);
-    if (id != SERVER_MESSAGE_CONNECTED)
-    {
-        g_warning ("Expected CONNECTED message, got %d", id);
-        return FALSE;
-    }
+    result = request->complete;
+    g_object_unref (request);
 
-    priv->connected = TRUE;
+    return result;
+}
 
-    return TRUE;
+/**
+ * lightdm_greeter_connect_sync:
+ * @greeter: The greeter to connect
+ * @error: return location for a #GError, or %NULL
+ *
+ * Connects the greeter to the display manager.  Will block until connected.
+ *
+ * Return value: #TRUE if successfully connected
+ *
+ * Deprecated: 1.11.1: Use lightdm_greeter_connect_to_daemon_sync() instead
+ **/
+gboolean
+lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error)
+{
+    return lightdm_greeter_connect_to_daemon_sync (greeter, error);
 }
 
 /**
@@ -542,7 +883,7 @@ lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error)
  *
  * Get a hint.
  *
- * Return value: The value for this hint or #NULL if not set.
+ * Return value: (nullable): The value for this hint or #NULL if not set.
  **/
 const gchar *
 lightdm_greeter_get_hint (LightDMGreeter *greeter, const gchar *name)
@@ -574,7 +915,7 @@ lightdm_greeter_get_default_session_hint (LightDMGreeter *greeter)
  * accounts should be taken from #LightDMUserList and displayed in the greeter
  * for the user to choose from.  Note that this list can be empty and it is
  * recommended you show a method for the user to enter a username manually.
- * 
+ *
  * If this option is shown the greeter should only allow these users to be
  * chosen for login unless the manual login hint is set.
  *
@@ -667,7 +1008,7 @@ lightdm_greeter_get_has_guest_account_hint (LightDMGreeter *greeter)
 
     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
     value = lightdm_greeter_get_hint (greeter, "has-guest-account");
-  
+
     return g_strcmp0 (value, "true") == 0;
 }
 
@@ -677,7 +1018,7 @@ lightdm_greeter_get_has_guest_account_hint (LightDMGreeter *greeter)
  *
  * Get the user to select by default.
  *
- * Return value: A username
+ * Return value: (nullable): A username or %NULL if no particular user should be selected.
  */
 const gchar *
 lightdm_greeter_get_select_user_hint (LightDMGreeter *greeter)
@@ -701,7 +1042,7 @@ lightdm_greeter_get_select_guest_hint (LightDMGreeter *greeter)
 
     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
     value = lightdm_greeter_get_hint (greeter, "select-guest");
-  
+
     return g_strcmp0 (value, "true") == 0;
 }
 
@@ -709,9 +1050,9 @@ lightdm_greeter_get_select_guest_hint (LightDMGreeter *greeter)
  * lightdm_greeter_get_autologin_user_hint:
  * @greeter: A #LightDMGreeter
  *
- * Get the user account to automatically logg into when the timer expires.
+ * Get the user account to automatically log into when the timer expires.
  *
- * Return value: The user account to automatically log into.
+ * Return value: (nullable): The user account to automatically log into or %NULL if none configured.
  */
 const gchar *
 lightdm_greeter_get_autologin_user_hint (LightDMGreeter *greeter)
@@ -735,7 +1076,7 @@ lightdm_greeter_get_autologin_guest_hint (LightDMGreeter *greeter)
 
     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
     value = lightdm_greeter_get_hint (greeter, "autologin-guest");
-  
+
     return g_strcmp0 (value, "true") == 0;
 }
 
@@ -805,7 +1146,7 @@ lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username)
 
     priv->cancelling_authentication = FALSE;
     priv->authenticate_sequence_number++;
-    priv->in_authentication = TRUE;  
+    priv->in_authentication = TRUE;
     priv->is_authenticated = FALSE;
     if (username != priv->authentication_user)
     {
@@ -817,7 +1158,7 @@ lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username)
     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE, int_length () + string_length (username), &offset);
     write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
     write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
-    write_message (greeter, message, offset);
+    send_message (greeter, message, offset);
 }
 
 /**
@@ -849,7 +1190,7 @@ lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter)
     g_debug ("Starting authentication for guest account...");
     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, int_length (), &offset);
     write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
-    write_message (greeter, message, offset);
+    send_message (greeter, message, offset);
 }
 
 /**
@@ -906,7 +1247,7 @@ lightdm_greeter_authenticate_remote (LightDMGreeter *greeter, const gchar *sessi
     write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
     write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
     write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
-    write_message (greeter, message, offset);
+    send_message (greeter, message, offset);
 }
 
 /**
@@ -936,27 +1277,23 @@ lightdm_greeter_respond (LightDMGreeter *greeter, const gchar *response)
 
     if (priv->n_responses_waiting == 0)
     {
-      guint32 msg_length;
-      GList *iter;
-
-      g_debug ("Providing response to display manager");
-
-      msg_length = int_length ();
-      for (iter = priv->responses_received; iter; iter = iter->next)
-      {
-          msg_length += string_length ((gchar *)iter->data);
-      }
-
-      write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONTINUE_AUTHENTICATION, msg_length, &offset);
-      write_int (message, MAX_MESSAGE_LENGTH, g_list_length (priv->responses_received), &offset);
-      for (iter = priv->responses_received; iter; iter = iter->next)
-      {
-          write_string (message, MAX_MESSAGE_LENGTH, (gchar *)iter->data, &offset);
-      }
-      write_message (greeter, message, offset);
-
-      g_list_free_full (priv->responses_received, g_free);
-      priv->responses_received = NULL;
+        guint32 msg_length;
+        GList *iter;
+
+        g_debug ("Providing response to display manager");
+
+        msg_length = int_length ();
+        for (iter = priv->responses_received; iter; iter = iter->next)
+            msg_length += string_length ((gchar *)iter->data);
+
+        write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONTINUE_AUTHENTICATION, msg_length, &offset);
+        write_int (message, MAX_MESSAGE_LENGTH, g_list_length (priv->responses_received), &offset);
+        for (iter = priv->responses_received; iter; iter = iter->next)
+            write_string (message, MAX_MESSAGE_LENGTH, (gchar *)iter->data, &offset);
+        send_message (greeter, message, offset);
+
+        g_list_free_full (priv->responses_received, g_free);
+        priv->responses_received = NULL;
     }
 }
 
@@ -981,7 +1318,7 @@ lightdm_greeter_cancel_authentication (LightDMGreeter *greeter)
 
     priv->cancelling_authentication = TRUE;
     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0, &offset);
-    write_message (greeter, message, offset);
+    send_message (greeter, message, offset);
 }
 
 /**
@@ -1020,7 +1357,7 @@ lightdm_greeter_get_is_authenticated (LightDMGreeter *greeter)
  *
  * Get the user that is being authenticated.
  *
- * Return value: The username of the authentication user being authenticated or #NULL if no authentication in progress.
+ * Return value: (nullable): The username of the authentication user being authenticated or #NULL if no authentication in progress.
  */
 const gchar *
 lightdm_greeter_get_authentication_user (LightDMGreeter *greeter)
@@ -1051,7 +1388,53 @@ lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language)
 
     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SET_LANGUAGE, string_length (language), &offset);
     write_string (message, MAX_MESSAGE_LENGTH, language, &offset);
-    write_message (greeter, message, offset);
+    send_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_start_session:
+ * @greeter: A #LightDMGreeter
+ * @session: (allow-none): The session to log into or #NULL to use the default.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: (allow-none): A #GAsyncReadyCallback to call when completed or %NULL.
+ * @user_data: (allow-none): data to pass to the @callback or %NULL.
+ *
+ * Asynchronously start a session for the authenticated user.
+ *
+ * When the operation is finished, @callback will be invoked. You can then call lightdm_greeter_start_session_finish() to get the result of the operation.
+ *
+ * See lightdm_greeter_start_session_sync() for the synchronous version.
+ **/
+void
+lightdm_greeter_start_session (LightDMGreeter *greeter, const gchar *session, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+    LightDMGreeterPrivate *priv;
+    Request *request;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    send_start_session (greeter, session);
+    request = request_new (cancellable, callback, user_data);
+    priv->start_session_requests = g_list_append (priv->start_session_requests, request);
+}
+
+/**
+ * lightdm_greeter_start_session_finish:
+ * @greeter: A #LightDMGreeter
+ * @result: A #GAsyncResult.
+ * @error: return location for a #GError, or %NULL
+ *
+ * Start a session for the authenticated user.
+ *
+ * Return value: TRUE if the session was started.
+ **/
+gboolean
+lightdm_greeter_start_session_finish (LightDMGreeter *greeter, GAsyncResult *result, GError **error)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    return REQUEST (result)->return_code == 0;
 }
 
 /**
@@ -1068,10 +1451,8 @@ gboolean
 lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *session, GError **error)
 {
     LightDMGreeterPrivate *priv;
-    guint8 message[MAX_MESSAGE_LENGTH];
-    guint8 *response;
-    gsize response_length, offset = 0;
-    guint32 id, return_code = 1;
+    Request *request;
+    guint32 return_code;
 
     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
 
@@ -1080,39 +1461,168 @@ lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *sessio
     g_return_val_if_fail (priv->connected, FALSE);
     g_return_val_if_fail (priv->is_authenticated, FALSE);
 
-    if (session)
-        g_debug ("Starting session %s", session);
-    else
-        g_debug ("Starting default session");
+    /* Read until the session is started */
+    send_start_session (greeter, session);
+    request = request_new (NULL, NULL, NULL);
+    priv->start_session_requests = g_list_append (priv->start_session_requests, g_object_ref (request));
+    do
+    {
+        guint8 *message;
+        gsize message_length;
 
-    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_START_SESSION, string_length (session), &offset);
-    write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
-    write_message (greeter, message, offset);
+        message = recv_message (greeter, &message_length, TRUE);
+        if (!message)
+            break;
+        handle_message (greeter, message, message_length);
+        g_free (message);
+    } while (!request->complete);
 
-    response = read_message (greeter, &response_length, TRUE);
-    if (!response)
-        return FALSE;
+    return_code = request->return_code;
+    g_object_unref (request);
 
-    offset = 0;
-    id = read_int (response, response_length, &offset);
-    read_int (response, response_length, &offset);
-    if (id == SERVER_MESSAGE_SESSION_RESULT)
-        return_code = read_int (response, response_length, &offset);
-    else
-        g_warning ("Expected SESSION_RESULT message, got %d", id);
+    return return_code == 0;
+}
 
-    g_free (response);
+/**
+ * lightdm_greeter_ensure_shared_data_dir:
+ * @greeter: A #LightDMGreeter
+ * @username: A username
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: (allow-none): A #GAsyncReadyCallback to call when completed or %NULL.
+ * @user_data: (allow-none): data to pass to the @callback or %NULL.
+ *
+ * Ensure that a shared data dir for the given user is available.  Both the
+ * greeter user and @username will have write access to that folder.  The
+ * intention is that larger pieces of shared data would be stored there (files
+ * that the greeter creates but wants to give to a user -- like camera
+ * photos -- or files that the user creates but wants the greeter to
+ * see -- like contact avatars).
+ *
+ * LightDM will automatically create these if the user actually logs in, so
+ * greeters only need to call this method if they want to store something in
+ * the directory themselves.
+ **/
+void
+lightdm_greeter_ensure_shared_data_dir (LightDMGreeter *greeter, const gchar *username, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+    LightDMGreeterPrivate *priv;
+    Request *request;
 
-    return return_code == 0;
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    send_ensure_shared_data_dir (greeter, username);
+    request = request_new (cancellable, callback, user_data);
+    priv->ensure_shared_data_dir_requests = g_list_append (priv->ensure_shared_data_dir_requests, request);
+}
+
+/**
+ * lightdm_greeter_ensure_shared_data_dir_finish:
+ * @result: A #GAsyncResult.
+ * @greeter: A #LightDMGreeter
+ *
+ * Function to call from lightdm_greeter_ensure_shared_data_dir callback.
+ *
+ * Return value: The path to the shared directory, free with g_free.
+ **/
+gchar *
+lightdm_greeter_ensure_shared_data_dir_finish (LightDMGreeter *greeter, GAsyncResult *result)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+    return g_strdup (REQUEST (result)->dir);
+}
+
+/**
+ * lightdm_greeter_ensure_shared_data_dir_sync:
+ * @greeter: A #LightDMGreeter
+ * @username: A username
+ *
+ * Ensure that a shared data dir for the given user is available.  Both the
+ * greeter user and @username will have write access to that folder.  The
+ * intention is that larger pieces of shared data would be stored there (files
+ * that the greeter creates but wants to give to a user -- like camera
+ * photos -- or files that the user creates but wants the greeter to
+ * see -- like contact avatars).
+ *
+ * LightDM will automatically create these if the user actually logs in, so
+ * greeters only need to call this method if they want to store something in
+ * the directory themselves.
+ *
+ * Return value: The path to the shared directory, free with g_free.
+ **/
+gchar *
+lightdm_greeter_ensure_shared_data_dir_sync (LightDMGreeter *greeter, const gchar *username)
+{
+    LightDMGreeterPrivate *priv;
+    Request *request;
+    gchar *data_dir;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_val_if_fail (priv->connected, NULL);
+
+    /* Read until a response */
+    send_ensure_shared_data_dir (greeter, username);
+    request = request_new (NULL, NULL, NULL);
+    priv->ensure_shared_data_dir_requests = g_list_append (priv->ensure_shared_data_dir_requests, g_object_ref (request));
+    do
+    {
+        guint8 *message;
+        gsize message_length;
+
+        message = recv_message (greeter, &message_length, TRUE);
+        if (!message)
+            break;
+        handle_message (greeter, message, message_length);
+        g_free (message);
+    } while (!request->complete);
+
+    data_dir = g_strdup (request->dir);
+    g_object_unref (request);
+
+    return data_dir;
 }
 
 static void
 lightdm_greeter_init (LightDMGreeter *greeter)
 {
     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    const gchar *fd;
 
     priv->read_buffer = g_malloc (HEADER_SIZE);
     priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+    fd = g_getenv ("LIGHTDM_TO_SERVER_FD");
+    if (fd)
+    {
+        GError *error = NULL;
+
+        priv->to_server_channel = g_io_channel_unix_new (atoi (fd));
+        g_io_channel_set_encoding (priv->to_server_channel, NULL, &error);
+        if (error)
+            g_warning ("Failed to set encoding on to server channel to binary: %s\n", error->message);
+        g_clear_error (&error);
+    }
+    else
+        g_warning ("No LIGHTDM_TO_SERVER_FD environment variable");
+
+    fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
+    if (fd)
+    {
+        GError *error = NULL;
+
+        priv->from_server_channel = g_io_channel_unix_new (atoi (fd));
+        g_io_channel_set_encoding (priv->from_server_channel, NULL, &error);
+        if (error)
+            g_warning ("Failed to set encoding on from server channel to binary: %s\n", error->message);
+        g_clear_error (&error);
+        g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter);
+    }
+    else
+        g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable");
 }
 
 static void
@@ -1183,42 +1693,6 @@ lightdm_greeter_get_property (GObject    *object,
     }
 }
 
-static void
-marshal_VOID__STRING_INT (GClosure     *closure,
-                          GValue       *return_value G_GNUC_UNUSED,
-                          guint         n_param_values,
-                          const GValue *param_values,
-                          gpointer      invocation_hint G_GNUC_UNUSED,
-                          gpointer      marshal_data)
-{
-    typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer     data1,
-                                                   gpointer     arg_1,
-                                                   gint         arg_2,
-                                                   gpointer     data2);
-    register GMarshalFunc_VOID__STRING_INT callback;
-    register GCClosure *cc = (GCClosure*) closure;
-    register gpointer data1, data2;
-
-    g_return_if_fail (n_param_values == 3);
-
-    if (G_CCLOSURE_SWAP_DATA (closure))
-    {
-        data1 = closure->data;
-        data2 = g_value_peek_pointer (param_values + 0);
-    }
-    else
-    {
-        data1 = g_value_peek_pointer (param_values + 0);
-        data2 = closure->data;
-    }
-    callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback);
-
-    callback (data1,
-              (param_values + 1)->data[0].v_pointer,
-              (param_values + 2)->data[0].v_int,
-              data2);
-}
-
 static void
 lightdm_greeter_finalize (GObject *object)
 {
@@ -1252,7 +1726,7 @@ lightdm_greeter_class_init (LightDMGreeterClass *klass)
                                                           "default-session-hint",
                                                           "Default session hint",
                                                           NULL,
-                                                          G_PARAM_READWRITE));
+                                                          G_PARAM_READABLE));
 
     g_object_class_install_property (object_class,
                                      PROP_HIDE_USERS_HINT,
@@ -1370,13 +1844,13 @@ lightdm_greeter_class_init (LightDMGreeterClass *klass)
      * lightdm_greeter_cancel_authentication() to abort the authentication.
      **/
     signals[SHOW_PROMPT] =
-        g_signal_new ("show-prompt",
+        g_signal_new (LIGHTDM_GREETER_SIGNAL_SHOW_PROMPT,
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
                       G_STRUCT_OFFSET (LightDMGreeterClass, show_prompt),
                       NULL, NULL,
-                      marshal_VOID__STRING_INT,
-                      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+                      NULL,
+                      G_TYPE_NONE, 2, G_TYPE_STRING, lightdm_prompt_type_get_type ());
 
     /**
      * LightDMGreeter::show-message:
@@ -1388,13 +1862,13 @@ lightdm_greeter_class_init (LightDMGreeterClass *klass)
      * should show a message to the user.
      **/
     signals[SHOW_MESSAGE] =
-        g_signal_new ("show-message",
+        g_signal_new (LIGHTDM_GREETER_SIGNAL_SHOW_MESSAGE,
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
                       G_STRUCT_OFFSET (LightDMGreeterClass, show_message),
                       NULL, NULL,
-                      marshal_VOID__STRING_INT,
-                      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+                      NULL,
+                      G_TYPE_NONE, 2, G_TYPE_STRING, lightdm_message_type_get_type ());
 
     /**
      * LightDMGreeter::authentication-complete:
@@ -1407,12 +1881,12 @@ lightdm_greeter_class_init (LightDMGreeterClass *klass)
      * was successful.
      **/
     signals[AUTHENTICATION_COMPLETE] =
-        g_signal_new ("authentication-complete",
+        g_signal_new (LIGHTDM_GREETER_SIGNAL_AUTHENTICATION_COMPLETE,
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
                       G_STRUCT_OFFSET (LightDMGreeterClass, authentication_complete),
                       NULL, NULL,
-                      g_cclosure_marshal_VOID__VOID,
+                      NULL,
                       G_TYPE_NONE, 0);
 
     /**
@@ -1423,11 +1897,91 @@ lightdm_greeter_class_init (LightDMGreeterClass *klass)
      * The application should then call lightdm_greeter_login().
      **/
     signals[AUTOLOGIN_TIMER_EXPIRED] =
-        g_signal_new ("autologin-timer-expired",
+        g_signal_new (LIGHTDM_GREETER_SIGNAL_AUTOLOGIN_TIMER_EXPIRED,
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
                       G_STRUCT_OFFSET (LightDMGreeterClass, autologin_timer_expired),
                       NULL, NULL,
-                      g_cclosure_marshal_VOID__VOID,
+                      NULL,
+                      G_TYPE_NONE, 0);
+
+    /**
+     * LightDMGreeter::idle:
+     * @greeter: A #LightDMGreeter
+     *
+     * The ::idle signal gets emitted when the user has logged in and the
+     * greeter is no longer needed.
+     *
+     * This signal only matters if the greeter has marked itself as
+     * resettable using lightdm_greeter_set_resettable().
+     **/
+    signals[IDLE] =
+        g_signal_new (LIGHTDM_GREETER_SIGNAL_IDLE,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMGreeterClass, idle),
+                      NULL, NULL,
+                      NULL,
+                      G_TYPE_NONE, 0);
+
+    /**
+     * LightDMGreeter::reset:
+     * @greeter: A #LightDMGreeter
+     *
+     * The ::reset signal gets emitted when the user is returning to a greeter
+     * that was previously marked idle.
+     *
+     * This signal only matters if the greeter has marked itself as
+     * resettable using lightdm_greeter_set_resettable().
+     **/
+    signals[RESET] =
+        g_signal_new (LIGHTDM_GREETER_SIGNAL_RESET,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMGreeterClass, reset),
+                      NULL, NULL,
+                      NULL,
                       G_TYPE_NONE, 0);
 }
+
+static void
+request_init (Request *request)
+{
+}
+
+static void
+request_finalize (GObject *object)
+{
+    Request *request = REQUEST (object);
+
+    g_free (request->dir);
+    g_clear_object (&request->cancellable);
+
+    G_OBJECT_CLASS (request_parent_class)->finalize (object);
+}
+
+static void
+request_class_init (RequestClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    object_class->finalize = request_finalize;
+}
+
+static gpointer
+request_get_user_data (GAsyncResult *result)
+{
+    return REQUEST (result)->user_data;
+}
+
+static GObject *
+request_get_source_object (GAsyncResult *res)
+{
+    return NULL;
+}
+
+static void
+request_iface_init (GAsyncResultIface *iface)
+{
+    iface->get_user_data = request_get_user_data;
+    iface->get_source_object = request_get_source_object;
+}