]> rtime.felk.cvut.cz Git - sojka/lightdm.git/commitdiff
Merge with trunk
authorRobert Ancell <robert.ancell@canonical.com>
Mon, 28 Apr 2014 23:25:28 +0000 (11:25 +1200)
committerRobert Ancell <robert.ancell@canonical.com>
Mon, 28 Apr 2014 23:25:28 +0000 (11:25 +1200)
1  2 
liblightdm-gobject/greeter.c
liblightdm-gobject/liblightdm-gobject-1.vapi
liblightdm-gobject/lightdm/greeter.h
tests/src/test-gobject-greeter.c

index 158d573cd1d0f7d2bb4a532aa9e463d21ef52711,d02890cda604819303f0e5186d0a7e6cc5fa8022..327ffec0fb39e60d8b716af643d7b5c623a8328b
@@@ -39,12 -39,17 +39,17 @@@ enum 
      SHOW_MESSAGE,
      AUTHENTICATION_COMPLETE,
      AUTOLOGIN_TIMER_EXPIRED,
+     IDLE,
+     RESET,
      LAST_SIGNAL
  };
  static guint signals[LAST_SIGNAL] = { 0 };
  
  typedef struct
  {
+     /* TRUE if the daemon can reuse this greeter */
+     gboolean resettable;
      /* Channel to write to daemon */
      GIOChannel *to_server_channel;
  
@@@ -70,9 -75,6 +75,9 @@@
      /* Pending ensure shared data dir requests */
      GList *ensure_shared_data_dir_requests;
  
 +    /* Mapping of username to shared data directories */
 +    GHashTable *shared_data_dirs;
 +
      /* Hints provided by the daemon */
      GHashTable *hints;
  
@@@ -115,15 -117,14 +120,17 @@@ typedef enu
      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;
 +    GCancellable *cancellable;
 +    GAsyncReadyCallback callback;
 +    gpointer user_data;
      gboolean complete;
      guint32 return_code;
      gchar *dir;
@@@ -133,9 -134,8 +140,9 @@@ typedef struc
      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));
  
  /**
   * lightdm_greeter_new:
@@@ -150,32 -150,37 +157,53 @@@ 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 (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 gboolean
 +request_call_callback (Request *request)
 +{
 +    if (!request->callback)
 +        return FALSE;
 +
 +    if (request->cancellable && g_cancellable_is_cancelled (request->cancellable))
 +        return FALSE;
 +
 +    return TRUE;
 +}
 +
  static gboolean
  timed_login_cb (gpointer data)
  {
@@@ -283,17 -288,14 +311,17 @@@ get_message_length (guint8 *message, gs
      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;
      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
      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;
      }
  
      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);
 +    if (status != G_IO_STATUS_NORMAL)
 +        return FALSE;
 +
 +    g_debug ("Wrote %zi bytes to daemon", message_length);
      g_io_channel_flush (priv->to_server_channel, NULL);
 +
 +    return TRUE;
  }
  
  static void
@@@ -353,13 -351,10 +381,13 @@@ handle_connected (LightDMGreeter *greet
          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;
 +        if (request_call_callback (request))
 +            request->callback (G_OBJECT (greeter), G_ASYNC_RESULT (request), request->user_data);
          priv->connect_requests = g_list_remove (priv->connect_requests, request);
          g_object_unref (request);
      }
@@@ -469,20 -464,48 +497,51 @@@ handle_end_authentication (LightDMGreet
      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;
 +        if (request_call_callback (request))
 +            request->callback (G_OBJECT (greeter), G_ASYNC_RESULT (request), request->user_data);
          priv->start_session_requests = g_list_remove (priv->start_session_requests, request);
          g_object_unref (request);
      }
@@@ -494,7 -517,6 +553,7 @@@ handle_shared_dir_result (LightDMGreete
      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 = NULL;
          }
          request->complete = TRUE;
 +        if (request_call_callback (request))
 +            request->callback (G_OBJECT (greeter), G_ASYNC_RESULT (request), request->user_data);
          priv->ensure_shared_data_dir_requests = g_list_remove (priv->ensure_shared_data_dir_requests, request);
          g_object_unref (request);
      }
@@@ -538,6 -558,12 +597,12 @@@ handle_message (LightDMGreeter *greeter
      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)
          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);
          }
      }
  
@@@ -611,7 -634,7 +676,7 @@@ from_server_cb (GIOChannel *source, GIO
      gsize message_length;
  
      /* Read one message and process it */
 -    message = read_message (greeter, &message_length, FALSE);
 +    message = recv_message (greeter, &message_length, FALSE);
      if (message)
      {
          handle_message (greeter, message, message_length);
      return TRUE;
  }
  
 -static void
 +static gboolean
- send_connect (LightDMGreeter *greeter)
+ 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), &offset);
+     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION) + int_length (), &offset);
      write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
 -    write_message (greeter, message, offset);
+     write_int (message, MAX_MESSAGE_LENGTH, resettable ? 1 : 0, &offset);
++
 +    return send_message (greeter, message, offset);
  }
  
 -static void
 +static gboolean
  send_start_session (LightDMGreeter *greeter, const gchar *session)
  {
      guint8 message[MAX_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);
 +    return send_message (greeter, message, offset);
  }
  
 -static void
 +static gboolean
  send_ensure_shared_data_dir (LightDMGreeter *greeter, const gchar *username)
  {
      guint8 message[MAX_MESSAGE_LENGTH];
  
      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 send_message (greeter, message, offset);
 +}
 +
 +/**
 + * lightdm_greeter_connect:
 + * @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_finish() to get the result of the operation.
 + *
 + * See lightdm_greeter_connect_sync() for the synchronous version.
 + **/
 +void
 +lightdm_greeter_connect (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);
++    send_connect (greeter, priv->resettable);
 +}
 +
 +/**
 + * lightdm_greeter_connect_finish:
 + * @result: A #GAsyncResult.
 + * @error: return location for a #GError, or %NULL
 + *
 + * Finishes an operation started with lightdm_greeter_connect().
 + *
 + * Return value: #TRUE if successfully connected
 + **/
 +gboolean
 +lightdm_greeter_connect_finish (LightDMGreeter *greeter, GAsyncResult *result, GError **error)
 +{
 +    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
 +    return REQUEST (result)->complete;
  }
  
  /**
@@@ -719,22 -699,42 +786,22 @@@ gboolea
  lightdm_greeter_connect_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);
+     send_connect (greeter, priv->resettable);
 -    request = request_new ();
 +    request = request_new (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);
 +        message = recv_message (greeter, &message_length, TRUE);
          if (!message)
              break;
          handle_message (greeter, message, message_length);
@@@ -1028,7 -1028,7 +1095,7 @@@ lightdm_greeter_authenticate (LightDMGr
      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);
  }
  
  /**
@@@ -1060,7 -1060,7 +1127,7 @@@ lightdm_greeter_authenticate_as_guest (
      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);
  }
  
  /**
@@@ -1117,7 -1117,7 +1184,7 @@@ lightdm_greeter_authenticate_remote (Li
      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);
  }
  
  /**
@@@ -1160,7 -1160,7 +1227,7 @@@ lightdm_greeter_respond (LightDMGreete
          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);
 +        send_message (greeter, message, offset);
  
          g_list_free_full (priv->responses_received, g_free);
          priv->responses_received = NULL;
@@@ -1188,7 -1188,7 +1255,7 @@@ lightdm_greeter_cancel_authentication (
  
      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);
  }
  
  /**
@@@ -1258,53 -1258,7 +1325,53 @@@ lightdm_greeter_set_language (LightDMGr
  
      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;
  }
  
  /**
@@@ -1333,14 -1287,14 +1400,14 @@@ lightdm_greeter_start_session_sync (Lig
  
      /* Read until the session is started */
      send_start_session (greeter, session);
 -    request = request_new ();
 +    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;
  
 -        message = read_message (greeter, &message_length, TRUE);
 +        message = recv_message (greeter, &message_length, TRUE);
          if (!message)
              break;
          handle_message (greeter, message, message_length);
      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;
 +
 +    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
 + *
 + *
 + * 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
   * 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)
  
      /* Read until a response */
      send_ensure_shared_data_dir (greeter, username);
 -    request = request_new ();
 +    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 = read_message (greeter, &message_length, TRUE);
 +        message = recv_message (greeter, &message_length, TRUE);
          if (!message)
              break;
          handle_message (greeter, message, message_length);
@@@ -1459,29 -1364,9 +1526,29 @@@ static voi
  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)
 +    {
 +        priv->to_server_channel = g_io_channel_unix_new (atoi (fd));
 +        g_io_channel_set_encoding (priv->to_server_channel, NULL, NULL);
 +    }
 +    else
 +        g_warning ("No LIGHTDM_TO_SERVER_FD environment variable");
 +
 +    fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
 +    if (fd)
 +    {
 +        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);
 +    }
 +    else
 +        g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable");
  }
  
  static void
@@@ -1762,6 -1647,44 +1829,44 @@@ lightdm_greeter_class_init (LightDMGree
                        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 ("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 ("reset",
+                       G_TYPE_FROM_CLASS (klass),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET (LightDMGreeterClass, reset),
+                       NULL, NULL,
+                       NULL,
                        G_TYPE_NONE, 0);
  }
  
@@@ -1776,8 -1699,6 +1881,8 @@@ request_finalize (GObject *object
      Request *request = REQUEST (object);
  
      g_free (request->dir);
 +    if (request->cancellable)
 +        g_object_unref (request->cancellable);
  
      G_OBJECT_CLASS (request_parent_class)->finalize (object);
  }
@@@ -1788,29 -1709,3 +1893,29 @@@ 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 gboolean
 +request_is_tagged (GAsyncResult *res, gpointer source_tag)
 +{
 +    return FALSE;
 +}
 +
 +static void
 +request_iface_init (GAsyncResultIface *iface)
 +{
 +    iface->get_user_data = request_get_user_data;
 +    iface->get_source_object = request_get_source_object;
 +    iface->is_tagged = request_is_tagged;
 +}
index 6aabf3397b7b0f8281df328c4d74b3a53227eaa7,ea4cecd2486304ebc3e463634e9f1457a9f0b306..26f1205d0c784368571933e62da866c8a964f903
@@@ -24,7 -24,6 +24,7 @@@ namespace LightDM 
          public signal void authentication_complete ();
          public signal void autologin_timer_expired ();
  
 +        public async bool connect_sync () throws GLib.Error;
          public bool connect_sync () throws GLib.Error;
          public unowned string get_hint (string name);
          public unowned string default_session_hint { get; }
          public bool in_authentication { get; }
          public bool is_authenticated { get; }
          public unowned string? authentication_user { get; }
-         public void start_session_sync (string? session = null) throws GLib.Error;
 +        public async void start_session (string? session = null) throws GLib.Error;
+         public bool start_session_sync (string? session = null) throws GLib.Error;
 +        public async string ensure_shared_data_dir (string username);
 +        public string ensure_shared_data_dir_sync (string username);
      }
      [CCode (has_type_id = false)]
      public enum MessageType {
index f43aaebdccfe503ea9da74c2f1f127656a45f826,5fcf4fe340d5289673600f49ab5373b9789face5..dde714257d16436e7cddd1c7e48c2245efa92092
  #define LIGHTDM_GREETER_H_
  
  #include <glib-object.h>
 +#include <gio/gio.h>
  
  G_BEGIN_DECLS
  
  #define LIGHTDM_TYPE_GREETER            (lightdm_greeter_get_type())
 -#define LIGHTDM_GREETER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIGHTDM_TYPE_GREETER, LightDMGreeter));
 +#define LIGHTDM_GREETER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIGHTDM_TYPE_GREETER, LightDMGreeter))
  #define LIGHTDM_GREETER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), LIGHTDM_TYPE_GREETER, LightDMGreeterClass))
  #define LIGHTDM_IS_GREETER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LIGHTDM_TYPE_GREETER))
  #define LIGHTDM_IS_GREETER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LIGHTDM_TYPE_GREETER))
@@@ -58,24 -57,22 +58,26 @@@ typedef struc
      void (*show_prompt)(LightDMGreeter *greeter, const gchar *text, LightDMPromptType type);
      void (*authentication_complete)(LightDMGreeter *greeter);
      void (*autologin_timer_expired)(LightDMGreeter *greeter);
+     void (*idle)(LightDMGreeter *greeter);
+     void (*reset)(LightDMGreeter *greeter);
  
      /* Reserved */
      void (*reserved1) (void);
      void (*reserved2) (void);
      void (*reserved3) (void);
      void (*reserved4) (void);
-     void (*reserved5) (void);
-     void (*reserved6) (void);
  } LightDMGreeterClass;
  
  GType lightdm_greeter_get_type (void);
  
  LightDMGreeter *lightdm_greeter_new (void);
  
+ void lightdm_greeter_set_resettable (LightDMGreeter *greeter, gboolean resettable);
 +void lightdm_greeter_connect (LightDMGreeter *greeter, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 +
 +gboolean lightdm_greeter_connect_finish (LightDMGreeter *greeter, GAsyncResult *result, GError **error);
 +
  gboolean lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error);
  
  const gchar *lightdm_greeter_get_hint (LightDMGreeter *greeter, const gchar *name);
@@@ -124,16 -121,8 +126,16 @@@ const gchar *lightdm_greeter_get_authen
  
  void lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language);
  
 +void lightdm_greeter_start_session (LightDMGreeter *greeter, const gchar *session, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 +
 +gboolean lightdm_greeter_start_session_finish (LightDMGreeter *greeter, GAsyncResult *result, GError **error);
 +
  gboolean lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *session, GError **error);
  
 +void lightdm_greeter_ensure_shared_data_dir (LightDMGreeter *greeter, const gchar *username, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 +
 +gchar *lightdm_greeter_ensure_shared_data_dir_finish (LightDMGreeter *greeter, GAsyncResult *result);
 +
  gchar *lightdm_greeter_ensure_shared_data_dir_sync (LightDMGreeter *greeter, const gchar *username);
  
  G_END_DECLS
index fe790b4e46f7f35c36faac42de53df09384db1b2,68bdd20457c0dce279def790f5feb6286773879a..9b0c43a1b586228418f9c488fe82a73a3e490ee5
@@@ -10,7 -10,6 +10,7 @@@
  
  #include "status.h"
  
 +static int exit_code = EXIT_SUCCESS;
  static gchar *greeter_id;
  static GMainLoop *loop;
  static LightDMGreeter *greeter;
@@@ -65,76 -64,44 +65,108 @@@ sigterm_cb (gpointer user_data
      return TRUE;
  }
  
+ static void
+ notify_hints (LightDMGreeter *greeter)
+ {
+     if (lightdm_greeter_get_select_user_hint (greeter))
+         status_notify ("%s SELECT-USER-HINT USERNAME=%s", greeter_id, lightdm_greeter_get_select_user_hint (greeter));
+     if (lightdm_greeter_get_select_guest_hint (greeter))
+         status_notify ("%s SELECT-GUEST-HINT", greeter_id);
+     if (lightdm_greeter_get_lock_hint (greeter))
+         status_notify ("%s LOCK-HINT", greeter_id);
+     if (!lightdm_greeter_get_has_guest_account_hint (greeter))
+         status_notify ("%s HAS-GUEST-ACCOUNT-HINT=FALSE", greeter_id);
+     if (lightdm_greeter_get_hide_users_hint (greeter))
+         status_notify ("%s HIDE-USERS-HINT", greeter_id);
+     if (lightdm_greeter_get_show_manual_login_hint (greeter))
+         status_notify ("%s SHOW-MANUAL-LOGIN-HINT", greeter_id);
+     if (!lightdm_greeter_get_show_remote_login_hint (greeter))
+         status_notify ("%s SHOW-REMOTE-LOGIN-HINT=FALSE", greeter_id);
+ }
+ static void
+ idle_cb (LightDMGreeter *greeter)
+ {
+     status_notify ("%s IDLE", greeter_id);
+ }
+ static void
+ reset_cb (LightDMGreeter *greeter)
+ {
+     status_notify ("%s RESET", greeter_id);
+     notify_hints (greeter);
+ }
  static void
  user_changed_cb (LightDMUser *user)
  {
      status_notify ("%s USER-CHANGED USERNAME=%s", greeter_id, lightdm_user_get_name (user));
  }
  
 +static void
 +start_session_finished (GObject *object, GAsyncResult *result, gpointer data)
 +{
 +    LightDMGreeter *greeter = LIGHTDM_GREETER (object);
 +    GError *error = NULL;
 +
 +    if (!lightdm_greeter_start_session_finish (greeter, result, &error))
 +        status_notify ("%s SESSION-FAILED", greeter_id);
 +    g_clear_error (&error);
 +}
 +
 +static void
 +write_shared_data_finished (GObject *object, GAsyncResult *result, gpointer data)
 +{
 +    LightDMGreeter *greeter = LIGHTDM_GREETER (object);
 +    gchar *dir, *path, *test_data;
 +    FILE *f;
 +
 +    dir = lightdm_greeter_ensure_shared_data_dir_finish (greeter, result);
 +    if (!dir)
 +    {
 +        status_notify ("%s WRITE-SHARED-DATA ERROR=NO_SHARED_DIR", greeter_id);
 +        return;
 +    }
 +
 +    path = g_build_filename (dir, "data", NULL);
 +    test_data = data;
 +    if (!(f = fopen (path, "w")) || fprintf (f, "%s", test_data) < 0)
 +        status_notify ("%s WRITE-SHARED-DATA ERROR=%s", greeter_id, strerror (errno));
 +    else
 +        status_notify ("%s WRITE-SHARED-DATA RESULT=TRUE", greeter_id);
 +    g_free (test_data);
 +
 +    if (f)
 +        fclose (f);
 +    g_free (path);
 +    g_free (dir);
 +}
 +
 +static void
 +read_shared_data_finished (GObject *object, GAsyncResult *result, gpointer data)
 +{
 +    LightDMGreeter *greeter = LIGHTDM_GREETER (object);
 +    gchar *dir, *path;
 +    gchar *contents = NULL;
 +    GError *error = NULL;
 +
 +    dir = lightdm_greeter_ensure_shared_data_dir_finish (greeter, result);
 +    if (!dir)
 +    {
 +        status_notify ("%s READ-SHARED-DATA ERROR=NO_SHARED_DIR", greeter_id);
 +        return;
 +    }
 +
 +    path = g_build_filename (dir, "data", NULL);
 +    if (g_file_get_contents (path, &contents, NULL, &error))
 +        status_notify ("%s READ-SHARED-DATA DATA=%s", greeter_id, contents);
 +    else
 +        status_notify ("%s READ-SHARED-DATA ERROR=%s", greeter_id, error->message);
 +    g_free (path);
 +    g_free (contents);
 +    g_clear_error (&error);
 +}
 +
  static void
  request_cb (const gchar *name, GHashTable *params)
  {
          lightdm_greeter_cancel_authentication (greeter);
  
      else if (strcmp (name, "START-SESSION") == 0)
 -    {
 -        if (!lightdm_greeter_start_session_sync (greeter, g_hash_table_lookup (params, "SESSION"), NULL))
 -            status_notify ("%s SESSION-FAILED", greeter_id);
 -    }
 +        lightdm_greeter_start_session (greeter, g_hash_table_lookup (params, "SESSION"), NULL, start_session_finished, NULL);
  
      else if (strcmp (name, "LOG-DEFAULT-SESSION") == 0)
          status_notify ("%s LOG-DEFAULT-SESSION SESSION=%s", greeter_id, lightdm_greeter_get_default_session_hint (greeter));
  
      else if (strcmp (name, "WRITE-SHARED-DATA") == 0)
      {
 -        gchar *dir;
 -
 -        dir = lightdm_greeter_ensure_shared_data_dir_sync (greeter, g_hash_table_lookup (params, "USERNAME"));
 -        if (dir)
 -        {
 -            gchar *path;
 -            FILE *f;
 -
 -            g_printerr ("dir='%s'\n", dir);
 -
 -            path = g_build_filename (dir, "data", NULL);
 -            if (!(f = fopen (path, "w")) || fprintf (f, "%s", (const gchar *) g_hash_table_lookup (params, "DATA")) < 0)
 -                status_notify ("%s WRITE-SHARED-DATA ERROR=%s", greeter_id, strerror (errno));
 -            else
 -                status_notify ("%s WRITE-SHARED-DATA RESULT=TRUE", greeter_id);
 -
 -            if (f)
 -                fclose (f);
 -            g_free (path);
 -            g_free (dir);
 -        }
 -        else
 -            status_notify ("%s WRITE-SHARED-DATA ERROR=NO_SHARED_DIR", greeter_id);
 +        const gchar *data = g_hash_table_lookup (params, "DATA");
 +        lightdm_greeter_ensure_shared_data_dir (greeter, g_hash_table_lookup (params, "USERNAME"), NULL, write_shared_data_finished, g_strdup (data));
      }
  
      else if (strcmp (name, "READ-SHARED-DATA") == 0)
 -    {
 -        gchar *dir;
 -
 -        dir = lightdm_greeter_ensure_shared_data_dir_sync (greeter, g_hash_table_lookup (params, "USERNAME"));
 -        if (dir)
 -        {
 -            gchar *path;
 -            gchar *contents = NULL;
 -            GError *error = NULL;
 -
 -            g_printerr ("dir='%s'\n", dir);
 -
 -            path = g_build_filename (dir, "data", NULL);
 -            if (g_file_get_contents (path, &contents, NULL, &error))
 -                status_notify ("%s READ-SHARED-DATA DATA=%s", greeter_id, contents);
 -            else
 -                status_notify ("%s READ-SHARED-DATA ERROR=%s", greeter_id, error->message);
 -            g_free (path);
 -            g_free (contents);
 -            g_clear_error (&error);
 -        }
 -        else
 -            status_notify ("%s READ-SHARED-DATA ERROR=NO_SHARED_DIR", greeter_id);
 -    }
 +        lightdm_greeter_ensure_shared_data_dir (greeter, g_hash_table_lookup (params, "USERNAME"), NULL, read_shared_data_finished, NULL);
  
      else if (strcmp (name, "WATCH-USER") == 0)
      {
@@@ -336,38 -350,6 +368,25 @@@ user_removed_cb (LightDMUserList *user_
      status_notify ("%s USER-REMOVED USERNAME=%s", greeter_id, lightdm_user_get_name (user));
  }
  
-     if (lightdm_greeter_get_select_user_hint (greeter))
-         status_notify ("%s SELECT-USER-HINT USERNAME=%s", greeter_id, lightdm_greeter_get_select_user_hint (greeter));
-     if (lightdm_greeter_get_select_guest_hint (greeter))
-         status_notify ("%s SELECT-GUEST-HINT", greeter_id);
-     if (lightdm_greeter_get_lock_hint (greeter))
-         status_notify ("%s LOCK-HINT", greeter_id);
-     if (!lightdm_greeter_get_has_guest_account_hint (greeter))
-         status_notify ("%s HAS-GUEST-ACCOUNT-HINT=FALSE", greeter_id);
-     if (lightdm_greeter_get_hide_users_hint (greeter))
-         status_notify ("%s HIDE-USERS-HINT", greeter_id);
-     if (lightdm_greeter_get_show_manual_login_hint (greeter))
-         status_notify ("%s SHOW-MANUAL-LOGIN-HINT", greeter_id);
-     if (!lightdm_greeter_get_show_remote_login_hint (greeter))
-         status_notify ("%s SHOW-REMOTE-LOGIN-HINT=FALSE", greeter_id);
 +static void
 +connect_finished (GObject *object, GAsyncResult *result, gpointer data)
 +{
 +    LightDMGreeter *greeter = LIGHTDM_GREETER (object);
 +    GError *error = NULL;
 +
 +    if (!lightdm_greeter_connect_finish (greeter, result, &error))
 +    {
 +        status_notify ("%s FAIL-CONNECT-DAEMON", greeter_id);
 +        exit_code = EXIT_FAILURE;
 +        g_main_loop_quit (loop);
 +        return;
 +    }
 +
 +    status_notify ("%s CONNECTED-TO-DAEMON", greeter_id);
 +
++    notify_hints (greeter);
 +}
 +
  int
  main (int argc, char **argv)
  {
      g_signal_connect (greeter, "show-prompt", G_CALLBACK (show_prompt_cb), NULL);
      g_signal_connect (greeter, "authentication-complete", G_CALLBACK (authentication_complete_cb), NULL);
      g_signal_connect (greeter, "autologin-timer-expired", G_CALLBACK (autologin_timer_expired_cb), NULL);
+     if (g_key_file_get_boolean (config, "test-greeter-config", "resettable", NULL))
+     {
+         lightdm_greeter_set_resettable (greeter, TRUE);
+         g_signal_connect (greeter, "idle", G_CALLBACK (idle_cb), NULL);
+         g_signal_connect (greeter, "reset", G_CALLBACK (reset_cb), NULL);
+     }
  
      if (g_key_file_get_boolean (config, "test-greeter-config", "log-user-changes", NULL))
      {
      }
  
      status_notify ("%s CONNECT-TO-DAEMON", greeter_id);
 -    if (!lightdm_greeter_connect_sync (greeter, NULL))
 -    {
 -        status_notify ("%s FAIL-CONNECT-DAEMON", greeter_id);
 -        return EXIT_FAILURE;
 -    }
 -
 -    status_notify ("%s CONNECTED-TO-DAEMON", greeter_id);
 -
 -    notify_hints (greeter);
 +    lightdm_greeter_connect (greeter, NULL, connect_finished, NULL);
  
      g_main_loop_run (loop);
  
 -    return EXIT_SUCCESS;
 +    return exit_code;
  }