]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blobdiff - liblightdm-gobject/greeter.c
Revert r2392 - it seems to have broken ABI in liblightdm-gobject
[sojka/lightdm.git] / liblightdm-gobject / greeter.c
index 8ce0cffcb10db9ed55c520171272ae3576c65fac..07aa2371f50881bc489240489e9eb7cc9bf0cab0 100644 (file)
 
 #include <stdlib.h>
 #include <string.h>
+#include <gio/gio.h>
+#include <gio/gunixsocketaddress.h>
 #include <security/pam_appl.h>
 
 #include "lightdm/greeter.h"
 
+G_DEFINE_QUARK (lightdm_greeter_error, lightdm_greeter_error)
+
 enum {
-    PROP_0,
-    PROP_DEFAULT_SESSION_HINT,
+    PROP_DEFAULT_SESSION_HINT = 1,
     PROP_HIDE_USERS_HINT,
     PROP_SHOW_MANUAL_LOGIN_HINT,
     PROP_SHOW_REMOTE_LOGIN_HINT,
@@ -39,26 +42,50 @@ 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;
+
+    /* Socket connection to daemon */
+    GSocket *socket;
+
+    /* 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;
+    guint from_server_watch;
+
+    /* 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;
@@ -97,14 +124,21 @@ typedef enum
     SERVER_MESSAGE_END_AUTHENTICATION,
     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;
+    LightDMGreeter *greeter;
+    GCancellable *cancellable;
+    GAsyncReadyCallback callback;
+    gpointer user_data;
     gboolean complete;
-    guint32 return_code;
+    gboolean result;
+    GError *error;
     gchar *dir;
 } Request;
 typedef struct
@@ -112,8 +146,66 @@ 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 (Request, request, G_TYPE_OBJECT);
+G_DEFINE_TYPE_WITH_CODE (Request, request, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, request_iface_init));
+
+static gboolean from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data);
+
+GType
+lightdm_greeter_error_get_type (void)
+{
+    static GType enum_type = 0;
+
+    if (G_UNLIKELY(enum_type == 0)) {
+        static const GEnumValue values[] = {
+            { LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR, "LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR", "communication-error" },
+            { LIGHTDM_GREETER_ERROR_CONNECTION_FAILED, "LIGHTDM_GREETER_ERROR_CONNECTION_FAILED", "connection-failed" },
+            { LIGHTDM_GREETER_ERROR_SESSION_FAILED, "LIGHTDM_GREETER_ERROR_SESSION_FAILED", "session-failed" },
+            { LIGHTDM_GREETER_ERROR_NO_AUTOLOGIN, "LIGHTDM_GREETER_ERROR_NO_AUTOLOGIN", "no-autologin" },
+            { LIGHTDM_GREETER_ERROR_INVALID_USER, "LIGHTDM_GREETER_ERROR_INVALID_USER", "invalid-user" },          
+            { 0, NULL, NULL }
+        };
+        enum_type = g_enum_register_static (g_intern_static_string ("LightDMGreeterError"), values);
+    }
+
+    return enum_type;
+}
+
+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:
@@ -123,21 +215,71 @@ G_DEFINE_TYPE (Request, request, G_TYPE_OBJECT);
  * 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 (void)
+request_new (LightDMGreeter *greeter, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 {
     Request *request;
 
     request = g_object_new (request_get_type (), NULL);
+    request->greeter = greeter;
+    if (cancellable)
+        request->cancellable = g_object_ref (cancellable);
+    request->callback = callback;
+    request->user_data = user_data;
 
     return request;
 }
 
+static gboolean
+request_callback_cb (gpointer data)
+{
+    Request *request = data;
+    if (request->callback)
+        request->callback (G_OBJECT (request->greeter), G_ASYNC_RESULT (request), request->user_data);
+    g_object_unref (request);
+    return G_SOURCE_REMOVE;
+}
+
+static void
+request_complete (Request *request)
+{
+    request->complete = TRUE;
+
+    if (!request->callback)
+        return;
+
+    if (request->cancellable && g_cancellable_is_cancelled (request->cancellable))
+        return;
+
+    g_idle_add (request_callback_cb, g_object_ref (request));
+}
+
 static gboolean
 timed_login_cb (gpointer data)
 {
@@ -156,30 +298,44 @@ int_length (void)
     return 4;
 }
 
-static void
-write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
+static gboolean
+write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset, GError **error)
 {
     if (*offset + 4 >= buffer_length)
-        return;
+    {
+        g_set_error_literal (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR,
+                             "Not enough buffer space to write integer");
+        return FALSE;
+    }
     buffer[*offset] = value >> 24;
     buffer[*offset+1] = (value >> 16) & 0xFF;
     buffer[*offset+2] = (value >> 8) & 0xFF;
     buffer[*offset+3] = value & 0xFF;
     *offset += 4;
+
+    return TRUE;
 }
 
-static void
-write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
+static gboolean
+write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset, GError **error)
 {
     gint length = 0;
 
     if (value)
         length = strlen (value);
-    write_int (buffer, buffer_length, length, offset);
+    if (!write_int (buffer, buffer_length, length, offset, error))
+        return FALSE;
     if (*offset + length >= buffer_length)
-        return;
-    memcpy (buffer + *offset, value, length);
+    {
+        g_set_error (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR,
+                     "Not enough buffer space to write string of length %d octets", length);
+        return FALSE;
+    }
+    if (value)
+        memcpy (buffer + *offset, value, length);
     *offset += length;
+
+    return TRUE;
 }
 
 static guint32
@@ -231,11 +387,11 @@ string_length (const gchar *value)
         return int_length ();
 }
 
-static void
-write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
+static gboolean
+write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset, GError **error)
 {
-    write_int (buffer, buffer_length, id, offset);
-    write_int (buffer, buffer_length, length, offset);
+    return write_int (buffer, buffer_length, id, offset, error) &&
+           write_int (buffer, buffer_length, length, offset, error);
 }
 
 static guint32
@@ -245,13 +401,69 @@ 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
+connect_to_daemon (LightDMGreeter *greeter, GError **error)
 {
     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
-    GIOStatus status;
-    GError *error = NULL;
+    const gchar *to_server_fd, *from_server_fd, *pipe_path;
+
+    if (priv->to_server_channel || priv->from_server_channel)
+        return TRUE;
+
+    /* Use private connection if one exists */  
+    to_server_fd = g_getenv ("LIGHTDM_TO_SERVER_FD");
+    from_server_fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
+    pipe_path = g_getenv ("LIGHTDM_GREETER_PIPE");
+    if (to_server_fd && from_server_fd)
+    {
+        priv->to_server_channel = g_io_channel_unix_new (atoi (to_server_fd));
+        priv->from_server_channel = g_io_channel_unix_new (atoi (from_server_fd));
+    }
+    else if (pipe_path)
+    {
+        GSocketAddress *address;
+        gboolean result;
+
+        priv->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, error);
+        if (!priv->socket)
+            return FALSE;
+
+        address = g_unix_socket_address_new (pipe_path);
+        result = g_socket_connect (priv->socket, address, NULL, error);
+        g_object_unref (address);
+        if (!result)
+            return FALSE;
+
+        priv->from_server_channel = g_io_channel_unix_new (g_socket_get_fd (priv->socket));
+        priv->to_server_channel = g_io_channel_ref (priv->from_server_channel);
+    }
+    else
+    {
+        g_set_error_literal (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_CONNECTION_FAILED,
+                             "Unable to determine socket to daemon");
+        return FALSE;
+    }
+
+    priv->from_server_watch = g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter);
+
+    if (!g_io_channel_set_encoding (priv->to_server_channel, NULL, error) ||
+        !g_io_channel_set_encoding (priv->from_server_channel, NULL, error))
+        return FALSE;
+
+    return TRUE;
+}
+
+static gboolean
+send_message (LightDMGreeter *greeter, guint8 *message, gsize message_length, GError **error)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    gchar *data;
+    gsize data_length;
     guint32 stated_length;
+    GError *flush_error = NULL;
+
+    if (!connect_to_daemon (greeter, error))
+        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
@@ -261,17 +473,45 @@ write_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
     stated_length = HEADER_SIZE + get_message_length (message, 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;
+        g_set_error (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR,
+                     "Refusing to write malformed packet to daemon: declared size is %u, but actual size is %zu",
+                     stated_length, message_length);
+        return FALSE;
+    }
+
+    data = (gchar *) message;
+    data_length = message_length;
+    while (data_length > 0)
+    {
+        GIOStatus status;
+        gsize n_written;
+        GError *write_error = NULL;
+
+        status = g_io_channel_write_chars (priv->to_server_channel, data, data_length, &n_written, &write_error);
+        if (write_error)
+            g_set_error (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR,
+                         "Failed to write to daemon: %s",
+                         write_error->message);
+        g_clear_error (&write_error);
+        if (status == G_IO_STATUS_AGAIN) 
+            continue;
+        if (status != G_IO_STATUS_NORMAL) 
+            return FALSE;
+        data_length -= n_written;
+        data += n_written;
+    }
+
+    g_debug ("Wrote %zi bytes to daemon", message_length);
+    if (!g_io_channel_flush (priv->to_server_channel, &flush_error))
+    {
+        g_set_error (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR,
+                     "Failed to write to daemon: %s",
+                     flush_error->message);
+        g_clear_error (&flush_error);
+        return FALSE;
     }
 
-    status = g_io_channel_write_chars (priv->to_server_channel, (gchar *) message, message_length, NULL, &error);
-    if (error)
-        g_warning ("Error writing 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
@@ -288,7 +528,7 @@ handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length
     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);
@@ -308,10 +548,12 @@ handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length
         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 = TRUE;
+        request->result = TRUE;
+        request_complete (request);
         priv->connect_requests = g_list_remove (priv->connect_requests, request);
         g_object_unref (request);
     }
@@ -421,17 +663,56 @@ 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 = TRUE;
+        guint32 return_code;
+
+        return_code = read_int (message, message_length, offset);
+        if (return_code == 0)
+            request->result = TRUE;
+        else
+            request->error = g_error_new (LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_SESSION_FAILED,
+                                          "Session returned error code %d", return_code);
+        request_complete (request);
         priv->start_session_requests = g_list_remove (priv->start_session_requests, request);
         g_object_unref (request);
     }
@@ -443,6 +724,7 @@ handle_shared_dir_result (LightDMGreeter *greeter, guint8 *message, gsize messag
     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
     Request *request;
 
+    /* Notify asynchronous caller */
     request = g_list_nth_data (priv->ensure_shared_data_dir_requests, 0);
     if (request)
     {
@@ -452,8 +734,10 @@ handle_shared_dir_result (LightDMGreeter *greeter, guint8 *message, gsize messag
         {
             g_free (request->dir);
             request->dir = NULL;
+            request->error = g_error_new (LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_INVALID_USER,
+                                          "No such user");
         }
-        request->complete = TRUE;
+        request_complete (request);
         priv->ensure_shared_data_dir_requests = g_list_remove (priv->ensure_shared_data_dir_requests, request);
         g_object_unref (request);
     }
@@ -484,19 +768,26 @@ handle_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
     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)
+static gboolean
+recv_message (LightDMGreeter *greeter, gboolean block, guint8 **message, gsize *length, GError **error)
 {
     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
     gsize n_to_read, n_read;
-    guint8 *buffer;
-    GError *error = NULL;
+
+    if (!connect_to_daemon (greeter, error))
+        return FALSE;
 
     /* Read the header, or the whole message if we already have that */
     n_to_read = HEADER_SIZE;
@@ -506,16 +797,26 @@ read_message (LightDMGreeter *greeter, gsize *length, gboolean block)
     do
     {
         GIOStatus status;
+        GError *read_error = NULL;
+
         status = g_io_channel_read_chars (priv->from_server_channel,
                                           (gchar *) priv->read_buffer + priv->n_read,
                                           n_to_read - priv->n_read,
                                           &n_read,
-                                          &error);
-        if (error)
-            g_warning ("Error reading from server: %s", error->message);
-        g_clear_error (&error);
-        if (status != G_IO_STATUS_NORMAL)
-            break;
+                                          &read_error);
+        if (status == G_IO_STATUS_AGAIN)
+        {
+            if (block)
+                continue;
+        }
+        else if (status != G_IO_STATUS_NORMAL)
+        {
+            g_set_error (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_COMMUNICATION_ERROR,
+                         "Failed to read from daemon: %s",
+                         read_error->message);
+            g_clear_error (&read_error);
+            return FALSE;
+        }
 
         g_debug ("Read %zi bytes from daemon", n_read);
 
@@ -524,7 +825,13 @@ 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 NULL;
+    {
+        if (message)
+            *message = NULL;
+        if (length)
+            *length = 0;
+        return TRUE;
+    }
 
     /* If have header, rerun for content */
     if (priv->n_read == HEADER_SIZE)
@@ -533,17 +840,21 @@ 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, block, message, length, error);
         }
     }
 
-    buffer = priv->read_buffer;
-    *length = priv->n_read;
+    if (message)
+        *message = priv->read_buffer;
+    else
+        g_free (priv->read_buffer);
+    if (length)
+        *length = priv->n_read;
 
     priv->read_buffer = g_malloc (priv->n_read);
     priv->n_read = 0;
 
-    return buffer;
+    return TRUE;
 }
 
 static gboolean
@@ -552,32 +863,41 @@ from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
     LightDMGreeter *greeter = data;
     guint8 *message;
     gsize message_length;
+    GError *error = NULL;
 
     /* Read one message and process it */
-    message = read_message (greeter, &message_length, FALSE);
+    if (!recv_message (greeter, FALSE, &message, &message_length, &error))
+    {
+        // FIXME: Should push this up to the client somehow
+        g_warning ("Failed to read from daemon: %s\n", error->message);
+        g_clear_error (&error);
+        return G_SOURCE_REMOVE;
+    }
+
     if (message)
     {
         handle_message (greeter, message, message_length);
         g_free (message);
     }
 
-    return TRUE;
+    return G_SOURCE_CONTINUE;
 }
 
-static void
-send_connect (LightDMGreeter *greeter)
+static gboolean
+send_connect (LightDMGreeter *greeter, gboolean resettable, GError **error)
 {
     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), &offset);
-    write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
-    write_message (greeter, message, offset);
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION) + int_length (), &offset, error) &&
+           write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset, error) &&
+           write_int (message, MAX_MESSAGE_LENGTH, resettable ? 1 : 0, &offset, error) &&
+           send_message (greeter, message, offset, error);
 }
 
-static void
-send_start_session (LightDMGreeter *greeter, const gchar *session)
+static gboolean
+send_start_session (LightDMGreeter *greeter, const gchar *session, GError **error)
 {
     guint8 message[MAX_MESSAGE_LENGTH];
     gsize offset = 0;
@@ -587,26 +907,83 @@ send_start_session (LightDMGreeter *greeter, const gchar *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);
-    write_message (greeter, message, offset);
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_START_SESSION, string_length (session), &offset, error) &&
+           write_string (message, MAX_MESSAGE_LENGTH, session, &offset, error) &&
+           send_message (greeter, message, offset, error);
 }
 
-static void
-send_ensure_shared_data_dir (LightDMGreeter *greeter, const gchar *username)
+static gboolean
+send_ensure_shared_data_dir (LightDMGreeter *greeter, const gchar *username, GError **error)
 {
     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);
-    write_message (greeter, message, offset);
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_ENSURE_SHARED_DIR, string_length (username), &offset, error) &&
+           write_string (message, MAX_MESSAGE_LENGTH, username, &offset, error) &&
+           send_message (greeter, message, offset, error);
 }
 
 /**
- * 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;
+    GError *error = NULL;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    request = request_new (greeter, cancellable, callback, user_data);
+    if (send_connect (greeter, priv->resettable, &error))
+        priv->connect_requests = g_list_append (priv->connect_requests, request);
+    else
+    {
+        request->error = error;
+        request_complete (request);
+        g_object_unref (request);
+    }
+}
+
+/**
+ * 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)
+{
+    Request *request = REQUEST (result);
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+
+    if (request->error)
+        g_propagate_error (error, request->error);
+    return request->result;
+}
+
+/**
+ * lightdm_greeter_connect_to_daemon_sync:
  * @greeter: The greeter to connect
  * @error: return location for a #GError, or %NULL
  *
@@ -615,54 +992,49 @@ send_ensure_shared_data_dir (LightDMGreeter *greeter, const gchar *username)
  * 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;
     Request *request;
 
     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)
-    {
-        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);
-
     /* Read until we are connected */
-    send_connect (greeter);
-    request = request_new ();
+    if (!send_connect (greeter, priv->resettable, error))
+        return FALSE;
+    request = request_new (greeter, NULL, NULL, NULL);
     priv->connect_requests = g_list_append (priv->connect_requests, g_object_ref (request));
     do
     {
         guint8 *message;
         gsize message_length;
 
-        message = read_message (greeter, &message_length, TRUE);
-        if (!message)
-            break;
+        if (!recv_message (greeter, TRUE, &message, &message_length, error))
+            return FALSE;
         handle_message (greeter, message, message_length);
         g_free (message);
     } while (!request->complete);
 
-    g_object_unref (request);
+    return lightdm_greeter_connect_to_daemon_finish (greeter, G_ASYNC_RESULT (request), error);
+}
 
-    return request->complete;
+/**
+ * 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);
 }
 
 /**
@@ -672,7 +1044,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)
@@ -704,7 +1076,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.
  *
@@ -797,7 +1169,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;
 }
 
@@ -807,7 +1179,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)
@@ -831,7 +1203,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;
 }
 
@@ -839,9 +1211,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)
@@ -865,7 +1237,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;
 }
 
@@ -917,25 +1289,28 @@ lightdm_greeter_cancel_autologin (LightDMGreeter *greeter)
  * lightdm_greeter_authenticate:
  * @greeter: A #LightDMGreeter
  * @username: (allow-none): A username or #NULL to prompt for a username.
+ * @error: return location for a #GError, or %NULL
  *
  * Starts the authentication procedure for a user.
+ *
+ * Return value: #TRUE if authentication request sent.
  **/
-void
-lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username)
+gboolean
+lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username, GError **error)
 {
     LightDMGreeterPrivate *priv;
     guint8 message[MAX_MESSAGE_LENGTH];
     gsize offset = 0;
 
-    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
 
     priv = GET_PRIVATE (greeter);
 
-    g_return_if_fail (priv->connected);
+    g_return_val_if_fail (priv->connected, FALSE);
 
     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)
     {
@@ -944,30 +1319,33 @@ lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username)
     }
 
     g_debug ("Starting authentication for user %s...", 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);
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE, int_length () + string_length (username), &offset, error) &&
+           write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset, error) &&
+           write_string (message, MAX_MESSAGE_LENGTH, username, &offset, error) &&
+           send_message (greeter, message, offset, error);
 }
 
 /**
  * lightdm_greeter_authenticate_as_guest:
  * @greeter: A #LightDMGreeter
+ * @error: return location for a #GError, or %NULL
  *
  * Starts the authentication procedure for the guest user.
+ *
+ * Return value: #TRUE if authentication request sent.
  **/
-void
-lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter)
+gboolean
+lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter, GError **error)
 {
     LightDMGreeterPrivate *priv;
     guint8 message[MAX_MESSAGE_LENGTH];
     gsize offset = 0;
 
-    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
 
     priv = GET_PRIVATE (greeter);
 
-    g_return_if_fail (priv->connected);
+    g_return_val_if_fail (priv->connected, FALSE);
 
     priv->cancelling_authentication = FALSE;
     priv->authenticate_sequence_number++;
@@ -977,27 +1355,36 @@ lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter)
     priv->authentication_user = NULL;
 
     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);
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, int_length (), &offset, error) &&
+           write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset, error) &&
+           send_message (greeter, message, offset, error);
 }
 
 /**
  * lightdm_greeter_authenticate_autologin:
  * @greeter: A #LightDMGreeter
+ * @error: return location for a #GError, or %NULL
  *
  * Starts the authentication procedure for the automatic login user.
+ *
+ * Return value: #TRUE if authentication request sent.
  **/
-void
-lightdm_greeter_authenticate_autologin (LightDMGreeter *greeter)
+gboolean
+lightdm_greeter_authenticate_autologin (LightDMGreeter *greeter, GError **error)
 {
     const gchar *user;
 
     user = lightdm_greeter_get_autologin_user_hint (greeter);
     if (lightdm_greeter_get_autologin_guest_hint (greeter))
-        lightdm_greeter_authenticate_as_guest (greeter);
+        return lightdm_greeter_authenticate_as_guest (greeter, error);
     else if (user)
-        lightdm_greeter_authenticate (greeter, user);
+        return lightdm_greeter_authenticate (greeter, user, error);
+    else
+    {
+        g_set_error_literal (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_NO_AUTOLOGIN,
+                             "Can't authenticate autologin; autologin not configured");
+        return FALSE;
+    }
 }
 
 /**
@@ -1005,21 +1392,24 @@ lightdm_greeter_authenticate_autologin (LightDMGreeter *greeter)
  * @greeter: A #LightDMGreeter
  * @session: The name of a remote session
  * @username: (allow-none): A username of #NULL to prompt for a username.
+ * @error: return location for a #GError, or %NULL
  *
  * Start authentication for a remote session type.
+ *
+ * Return value: #TRUE if authentication request sent.
  **/
-void
-lightdm_greeter_authenticate_remote (LightDMGreeter *greeter, const gchar *session, const gchar *username)
+gboolean
+lightdm_greeter_authenticate_remote (LightDMGreeter *greeter, const gchar *session, const gchar *username, GError **error)
 {
     LightDMGreeterPrivate *priv;
     guint8 message[MAX_MESSAGE_LENGTH];
     gsize offset = 0;
 
-    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
 
     priv = GET_PRIVATE (greeter);
 
-    g_return_if_fail (priv->connected);
+    g_return_val_if_fail (priv->connected, FALSE);
 
     priv->cancelling_authentication = FALSE;
     priv->authenticate_sequence_number++;
@@ -1032,86 +1422,96 @@ lightdm_greeter_authenticate_remote (LightDMGreeter *greeter, const gchar *sessi
         g_debug ("Starting authentication for remote session %s as user %s...", session, username);
     else
         g_debug ("Starting authentication for remote session %s...", session);
-    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_REMOTE, int_length () + string_length (session) + string_length (username), &offset);
-    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);
+
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_REMOTE, int_length () + string_length (session) + string_length (username), &offset, error) &&
+           write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset, error) &&
+           write_string (message, MAX_MESSAGE_LENGTH, session, &offset, error) &&
+           write_string (message, MAX_MESSAGE_LENGTH, username, &offset, error) &&
+           send_message (greeter, message, offset, error);
 }
 
 /**
  * lightdm_greeter_respond:
  * @greeter: A #LightDMGreeter
  * @response: Response to a prompt
+ * @error: return location for a #GError, or %NULL
  *
  * Provide response to a prompt.  May be one in a series.
+ *
+ * Return value: #TRUE if response sent.
  **/
-void
-lightdm_greeter_respond (LightDMGreeter *greeter, const gchar *response)
+gboolean
+lightdm_greeter_respond (LightDMGreeter *greeter, const gchar *response, GError **error)
 {
     LightDMGreeterPrivate *priv;
     guint8 message[MAX_MESSAGE_LENGTH];
     gsize offset = 0;
 
-    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
-    g_return_if_fail (response != NULL);
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    g_return_val_if_fail (response != NULL, FALSE);
 
     priv = GET_PRIVATE (greeter);
 
-    g_return_if_fail (priv->connected);
-    g_return_if_fail (priv->n_responses_waiting > 0);
+    g_return_val_if_fail (priv->connected, FALSE);
+    g_return_val_if_fail (priv->n_responses_waiting > 0, FALSE);
 
     priv->n_responses_waiting--;
     priv->responses_received = g_list_append (priv->responses_received, g_strdup (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);
+
+        if (!write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONTINUE_AUTHENTICATION, msg_length, &offset, error) ||
+            !write_int (message, MAX_MESSAGE_LENGTH, g_list_length (priv->responses_received), &offset, error))
+            return FALSE;
+        for (iter = priv->responses_received; iter; iter = iter->next)
+        {
+            if (!write_string (message, MAX_MESSAGE_LENGTH, (gchar *)iter->data, &offset, error))
+                return FALSE;
+        }
+        if (!send_message (greeter, message, offset, error))
+            return FALSE;
+
+        g_list_free_full (priv->responses_received, g_free);
+        priv->responses_received = NULL;
     }
+
+    return TRUE;
 }
 
 /**
  * lightdm_greeter_cancel_authentication:
  * @greeter: A #LightDMGreeter
+ * @error: return location for a #GError, or %NULL
  *
  * Cancel the current user authentication.
+ *
+ * Return value: #TRUE if cancel request sent.
  **/
-void
-lightdm_greeter_cancel_authentication (LightDMGreeter *greeter)
+gboolean
+lightdm_greeter_cancel_authentication (LightDMGreeter *greeter, GError **error)
 {
     LightDMGreeterPrivate *priv;
     guint8 message[MAX_MESSAGE_LENGTH];
     gsize offset = 0;
 
-    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
 
     priv = GET_PRIVATE (greeter);
 
-    g_return_if_fail (priv->connected);
+    g_return_val_if_fail (priv->connected, FALSE);
 
     priv->cancelling_authentication = TRUE;
-    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0, &offset);
-    write_message (greeter, message, offset);
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0, &offset, error) &&
+           send_message (greeter, message, offset, error);
 }
 
 /**
@@ -1150,7 +1550,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)
@@ -1163,25 +1563,84 @@ lightdm_greeter_get_authentication_user (LightDMGreeter *greeter)
  * lightdm_greeter_set_language:
  * @greeter: A #LightDMGreeter
  * @language: The language to use for this user in the form of a locale specification (e.g. "de_DE.UTF-8").
+ * @error: return location for a #GError, or %NULL
  *
  * Set the language for the currently authenticated user.
+ *
+ * Return value: #TRUE if set language request sent.
  **/
-void
-lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language)
+gboolean
+lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language, GError **error)
 {
     LightDMGreeterPrivate *priv;
     guint8 message[MAX_MESSAGE_LENGTH];
     gsize offset = 0;
 
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_val_if_fail (priv->connected, FALSE);
+
+    return write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SET_LANGUAGE, string_length (language), &offset, error) &&
+           write_string (message, MAX_MESSAGE_LENGTH, language, &offset, error) &&
+           send_message (greeter, message, offset, error);
+}
+
+/**
+ * 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;
+    GError *error = NULL;
+
     g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
 
     priv = GET_PRIVATE (greeter);
 
-    g_return_if_fail (priv->connected);
+    request = request_new (greeter, cancellable, callback, user_data);
+    priv->start_session_requests = g_list_append (priv->start_session_requests, request);
+    if (!send_start_session (greeter, session, &error))
+    {
+        request->error = error;
+        request_complete (request);
+    }
+}
 
-    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);
+/**
+ * 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)
+{
+    Request *request = REQUEST (result);
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+
+    if (request->error)  
+        g_propagate_error (error, request->error);
+    return request->result;
 }
 
 /**
@@ -1199,7 +1658,6 @@ lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *sessio
 {
     LightDMGreeterPrivate *priv;
     Request *request;
-    guint32 return_code;
 
     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
 
@@ -1209,31 +1667,90 @@ lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *sessio
     g_return_val_if_fail (priv->is_authenticated, FALSE);
 
     /* Read until the session is started */
-    send_start_session (greeter, session);
-    request = request_new ();
+    if (!send_start_session (greeter, session, error))
+        return FALSE;
+    request = request_new (greeter, NULL, NULL, NULL);
     priv->start_session_requests = g_list_append (priv->start_session_requests, g_object_ref (request));
     do
     {
         guint8 *message;
         gsize message_length;
 
-        message = read_message (greeter, &message_length, TRUE);
-        if (!message)
-            break;
+        if (!recv_message (greeter, TRUE, &message, &message_length, error))
+            return FALSE;
         handle_message (greeter, message, message_length);
         g_free (message);
     } while (!request->complete);
 
-    return_code = request->return_code;
-    g_object_unref (request);
+    return lightdm_greeter_start_session_finish (greeter, G_ASYNC_RESULT (request), error);
+}
 
-    return return_code == 0;
+/**
+ * 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;
+    GError *error = NULL;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    request = request_new (greeter, cancellable, callback, user_data);
+    priv->ensure_shared_data_dir_requests = g_list_append (priv->ensure_shared_data_dir_requests, request);
+    if (!send_ensure_shared_data_dir (greeter, username, &error))
+    {
+        request->error = error;
+        request_complete (request);
+    }
+}
+
+/**
+ * lightdm_greeter_ensure_shared_data_dir_finish:
+ * @result: A #GAsyncResult.
+ * @greeter: A #LightDMGreeter
+ * @error: return location for a #GError, or %NULL
+ *
+ * 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, GError **error)
+{
+    Request *request = REQUEST (result);
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+
+    if (request->error)
+        g_propagate_error (error, request->error);
+    return g_strdup (request->dir);
 }
 
 /**
  * lightdm_greeter_ensure_shared_data_dir_sync:
  * @greeter: A #LightDMGreeter
  * @username: A username
+ * @error: return location for a #GError, 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
@@ -1246,14 +1763,13 @@ lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *sessio
  * 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
+ * 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)
+lightdm_greeter_ensure_shared_data_dir_sync (LightDMGreeter *greeter, const gchar *username, GError **error)
 {
     LightDMGreeterPrivate *priv;
     Request *request;
-    gchar *data_dir;
 
     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
 
@@ -1262,25 +1778,22 @@ lightdm_greeter_ensure_shared_data_dir_sync (LightDMGreeter *greeter, const gcha
     g_return_val_if_fail (priv->connected, NULL);
 
     /* Read until a response */
-    send_ensure_shared_data_dir (greeter, username);
-    request = request_new ();
+    if (!send_ensure_shared_data_dir (greeter, username, error))
+        return NULL;
+    request = request_new (greeter, 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 = read_message (greeter, &message_length, TRUE);
-        if (!message)
-            break;
+        if (!recv_message (greeter, TRUE, &message, &message_length, error))
+            return FALSE;
         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;
+    return lightdm_greeter_ensure_shared_data_dir_finish (greeter, G_ASYNC_RESULT (request), error);
 }
 
 static void
@@ -1294,18 +1807,18 @@ lightdm_greeter_init (LightDMGreeter *greeter)
 
 static void
 lightdm_greeter_set_property (GObject      *object,
-                          guint         prop_id,
-                          const GValue *value,
-                          GParamSpec   *pspec)
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
 {
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
 static void
 lightdm_greeter_get_property (GObject    *object,
-                          guint       prop_id,
-                          GValue     *value,
-                          GParamSpec *pspec)
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
 {
     LightDMGreeter *self;
 
@@ -1366,12 +1879,26 @@ lightdm_greeter_finalize (GObject *object)
     LightDMGreeter *self = LIGHTDM_GREETER (object);
     LightDMGreeterPrivate *priv = GET_PRIVATE (self);
 
+    g_clear_object (&priv->socket);
     if (priv->to_server_channel)
         g_io_channel_unref (priv->to_server_channel);
     if (priv->from_server_channel)
         g_io_channel_unref (priv->from_server_channel);
-    g_free (priv->authentication_user);
+    if (priv->from_server_watch)
+        g_source_remove (priv->from_server_watch);
+    priv->from_server_watch = 0;
+    g_clear_pointer (&priv->read_buffer, g_free);
+    g_list_free_full (priv->responses_received, g_free);
+    priv->responses_received = NULL;
+    g_list_free_full (priv->connect_requests, g_object_unref);
+    priv->connect_requests = NULL;
+    g_list_free_full (priv->start_session_requests, g_object_unref);
+    priv->start_session_requests = NULL;
+    g_list_free_full (priv->ensure_shared_data_dir_requests, g_object_unref);
+    priv->ensure_shared_data_dir_requests = NULL;
+    g_clear_pointer (&priv->authentication_user, g_free);
     g_hash_table_unref (priv->hints);
+    priv->hints = NULL;
 
     G_OBJECT_CLASS (lightdm_greeter_parent_class)->finalize (object);
 }
@@ -1393,7 +1920,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,
@@ -1511,13 +2038,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,
                       NULL,
-                      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+                      G_TYPE_NONE, 2, G_TYPE_STRING, lightdm_prompt_type_get_type ());
 
     /**
      * LightDMGreeter::show-message:
@@ -1529,13 +2056,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,
                       NULL,
-                      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+                      G_TYPE_NONE, 2, G_TYPE_STRING, lightdm_message_type_get_type ());
 
     /**
      * LightDMGreeter::authentication-complete:
@@ -1548,7 +2075,7 @@ 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),
@@ -1564,13 +2091,51 @@ 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,
                       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
@@ -1583,6 +2148,7 @@ request_finalize (GObject *object)
 {
     Request *request = REQUEST (object);
 
+    g_clear_object (&request->cancellable);
     g_free (request->dir);
 
     G_OBJECT_CLASS (request_parent_class)->finalize (object);
@@ -1594,3 +2160,22 @@ 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 *result)
+{
+    return g_object_ref (REQUEST (result)->greeter);
+}
+
+static void
+request_iface_init (GAsyncResultIface *iface)
+{
+    iface->get_user_data = request_get_user_data;
+    iface->get_source_object = request_get_source_object;
+}