[PATCHES] proxy monitor fixes + mount by device file



Hey,

Here are two patches. The first one is very simple, it teaches
gvfs-mount about the --device (short -d) option so you can do things
like this

        $ gvfs-mount -d /dev/sdb3 
        The device "Generic STORAGE DEVICE" contains encrypted data on partition 3.
        Password: 
        Mounted /dev/dm-0 at /media/Encrypted Stuff
        
The other patch is, uh, a bit more complex. It's basically a huge set of
fixes for the proxy monitor code. I needed all these fixes for the
DeviceKit-disks/gnome-disk-utility volume monitor I'm working on. Here's
a bonus screenshot of that

 http://people.freedesktop.org/~david/gvfs-gdu-dkd-temp.png

Anyway, this patch

 1. Adds support for proxying GMountOperation to the remote VM

 2. Adds support for proxying GCancellable to the remote VM

 3. Makes each VM process track callers and cancels operations initiated
    by callers that disconnects from the bus

 4. Makes the client side proxy monitor track the remote monitor
    - if the VM disconnects all drives/volumes/mounts are removed
    - if the VM reconnects, we reseed the internal monitor and add
      the drives/volumes/mounts

 5. The VM now uses ALLOW_REPLACEMENT when claiming a name and also
    kills itself on NameLost (e.g. if it is replaced). Coupled with 4.
    above this makes it a lot more tolerable to hack on a remote volume
    monitor. Simply just compile it, start it and the existing
    (system-wide) copy will kill itself. And all the clients (Nautilus,
    panel, drive applet, gvfsd-computer) will reconnect and do the right
    thing.

 6. Makes the get_mount_for_mount_path() method on GNativeVolumeMonitor
    actually work. It turns out that at least gvfs-mount -u needs that.

 7. Uses /org/gtk/Private/RemoteVolumeMonitor instead of / as the object
    name. Cf. the version D-Bus debacle on Lennart's blog.

 8. Makes the proxy monitor client module resident

As you know most of this code is extremely hairy, it's all interprocess
communication, asynchronous operations and so on. I've tested it very
carefully so it should be good to commit. That said, some review on the
client side module changes (since that will affect virtually all GIO
clients) is appreciated. Thanks.

     David

Index: programs/gvfs-mount.c
===================================================================
--- programs/gvfs-mount.c	(revision 2238)
+++ programs/gvfs-mount.c	(working copy)
@@ -46,10 +46,12 @@
 static gboolean extra_detail = FALSE;
 static gboolean mount_monitor = FALSE;
 static const char *unmount_scheme = NULL;
+static const char *mount_device_file = NULL;
 
 static const GOptionEntry entries[] = 
 {
   { "mountable", 'm', 0, G_OPTION_ARG_NONE, &mount_mountable, "Mount as mountable", NULL },
+  { "device", 'd', 0, G_OPTION_ARG_STRING, &mount_device_file, "Mount volume with device file", NULL},
   { "unmount", 'u', 0, G_OPTION_ARG_NONE, &mount_unmount, "Unmount", NULL},
   { "unmount-scheme", 's', 0, G_OPTION_ARG_STRING, &unmount_scheme, "Unmount all mounts with the given scheme", NULL},
   { "list", 'l', 0, G_OPTION_ARG_NONE, &mount_list, "List", NULL},
@@ -193,8 +195,13 @@
   
   op = g_mount_operation_new ();
 
-  g_signal_connect (op, "ask_password", (GCallback)ask_password_cb, NULL);
+  g_signal_connect (op, "ask_password", G_CALLBACK (ask_password_cb), NULL);
 
+  /* TODO: we *should* also connect to the "aborted" signal but since we the
+   *       main thread is blocked handling input we won't get that signal
+   *       anyway...
+   */
+
   return op;
 }
 
@@ -630,6 +637,93 @@
 }
 
 static void
+mount_with_device_file_cb (GObject *object,
+                           GAsyncResult *res,
+                           gpointer user_data)
+{
+  GVolume *volume;
+  gboolean succeeded;
+  GError *error = NULL;
+
+  volume = G_VOLUME (object);
+
+  succeeded = g_volume_mount_finish (volume, res, &error);
+
+  if (!succeeded)
+    {
+      g_print ("Error mounting %s: %s\n",
+               g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE),
+               error->message);
+    }
+  else
+    {
+      GMount *mount;
+      GFile *root;
+      char *mount_path;
+
+      mount = g_volume_get_mount (volume);
+      root = g_mount_get_root (mount);
+      mount_path = g_file_get_path (root);
+
+      g_print ("Mounted %s at %s\n",
+               g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE),
+               mount_path);
+
+      g_object_unref (mount);
+      g_object_unref (root);
+      g_free (mount_path);
+    }
+
+  outstanding_mounts--;
+
+  if (outstanding_mounts == 0)
+    g_main_loop_quit (main_loop);
+}
+
+static void
+mount_with_device_file (const char *device_file)
+{
+  GVolumeMonitor *volume_monitor;
+  GList *volumes;
+  GList *l;
+
+  volume_monitor = g_volume_monitor_get();
+
+  volumes = g_volume_monitor_get_volumes (volume_monitor);
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      GVolume *volume = G_VOLUME (l->data);
+
+      if (g_strcmp0 (g_volume_get_identifier (volume,
+                                              G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE), device_file) == 0)
+        {
+          GMountOperation *op;
+
+          op = new_mount_op ();
+
+          g_volume_mount (volume,
+                          G_MOUNT_MOUNT_NONE,
+                          op,
+                          NULL,
+                          mount_with_device_file_cb,
+                          op);
+
+          outstanding_mounts++;
+        }
+    }
+  g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
+  g_list_free (volumes);
+
+  if (outstanding_mounts == 0)
+    {
+      g_print ("No volume for device file %s\n", device_file);
+      return;
+    }
+
+  g_object_unref (volume_monitor);
+}
+
+static void
 monitor_print_mount (GMount *mount)
 {
   if (extra_detail)
@@ -824,6 +918,10 @@
   
   if (mount_list)
     list_monitor_items ();
+  else if (mount_device_file != NULL)
+    {
+      mount_with_device_file (mount_device_file);
+    }
   else if (unmount_scheme != NULL)
     {
       unmount_all_with_scheme (unmount_scheme);

Index: monitor/proxy/gproxyvolume.c
===================================================================
--- monitor/proxy/gproxyvolume.c	(revision 2238)
+++ monitor/proxy/gproxyvolume.c	(working copy)
@@ -63,6 +63,8 @@
   gboolean should_automount;
 
   GProxyShadowMount *shadow_mount;
+
+  GHashTable *hash_mount_op_id_to_data;
 };
 
 static void g_proxy_volume_volume_iface_init (GVolumeIface *iface);
@@ -161,6 +163,7 @@
 static void
 g_proxy_volume_init (GProxyVolume *proxy_volume)
 {
+  proxy_volume->hash_mount_op_id_to_data = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 GProxyVolume *
@@ -672,32 +675,60 @@
 }
 
 typedef struct {
-  GObject *object;
+  GProxyVolume *volume;
   GAsyncReadyCallback callback;
   gpointer user_data;
+
+  gboolean is_cancelled;
+  gchar *cancellation_id;
   GCancellable *cancellable;
+  gulong cancelled_handler_id;
+
+  gchar *mount_op_id;
+  GMountOperation *mount_operation;
+  gulong reply_handler_id;
 } DBusOp;
 
 static void
 mount_cb (DBusMessage *reply,
-          GError *error,
-          DBusOp *data)
+          GError      *error,
+          DBusOp      *data)
 {
-  GSimpleAsyncResult *simple;
-  if (error != NULL)
-    simple = g_simple_async_result_new_from_error (data->object,
-                                                   data->callback,
-                                                   data->user_data,
-                                                   error);
-  else
-    simple = g_simple_async_result_new (data->object,
-                                        data->callback,
-                                        data->user_data,
-                                        NULL);
-  g_simple_async_result_complete_in_idle (simple);
-  g_object_unref (simple);
+  if (!data->is_cancelled)
+    {
+      GSimpleAsyncResult *simple;
 
-  g_object_unref (data->object);
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->volume),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->volume),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+
+  /* free DBusOp */
+  if (strlen (data->mount_op_id) > 0)
+    g_hash_table_remove (data->volume->hash_mount_op_id_to_data, data->mount_op_id);
+  g_object_unref (data->volume);
+
+  g_free (data->mount_op_id);
+  if (data->reply_handler_id > 0)
+    g_signal_handler_disconnect (data->mount_operation, data->reply_handler_id);
+  if (data->mount_operation != NULL)
+    g_object_unref (data->mount_operation);
+
+  g_free (data->cancellation_id);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
+
   g_free (data);
 }
 
@@ -720,6 +751,63 @@
 }
 
 static void
+cancel_operation_reply_cb (DBusMessage *reply,
+                           GError      *error,
+                           gpointer     user_data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from CancelOperation(): %s", error->message);
+    }
+}
+
+static void
+mount_cancelled (GCancellable *cancellable,
+                 gpointer      user_data)
+{
+  DBusOp *data = user_data;
+
+  if (!data->is_cancelled)
+    {
+      GSimpleAsyncResult *simple;
+      DBusConnection *connection;
+      DBusMessage *message;
+      const char *name;
+
+      data->is_cancelled = TRUE;
+
+      simple = g_simple_async_result_new_error (G_OBJECT (data->volume),
+                                                data->callback,
+                                                data->user_data,
+                                                G_IO_ERROR,
+                                                G_IO_ERROR_CANCELLED,
+                                                _("The operation was cancelled"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+
+      /* Now tell the remote volume monitor that the op has been cancelled */
+      connection = g_proxy_volume_monitor_get_dbus_connection (data->volume->volume_monitor);
+      name = g_proxy_volume_monitor_get_dbus_name (data->volume->volume_monitor);
+      message = dbus_message_new_method_call (name,
+                                              "/org/gtk/Private/RemoteVolumeMonitor",
+                                              "org.gtk.Private.RemoteVolumeMonitor",
+                                              "CancelOperation");
+      dbus_message_append_args (message,
+                                DBUS_TYPE_STRING,
+                                &(data->cancellation_id),
+                                DBUS_TYPE_INVALID);
+
+      _g_dbus_connection_call_async (connection,
+                                     message,
+                                     -1,
+                                     (GAsyncDBusCallback) cancel_operation_reply_cb,
+                                     NULL);
+      dbus_message_unref (message);
+      dbus_connection_unref (connection);
+    }
+}
+
+static void
 g_proxy_volume_mount (GVolume             *volume,
                       GMountMountFlags     flags,
                       GMountOperation     *mount_operation,
@@ -760,43 +848,297 @@
       const char *name;
       DBusMessage *message;
       dbus_uint32_t _flags = flags;
-      dbus_bool_t use_mount_operation = mount_operation != NULL;
 
-      /* TODO: support mount_operation */
-
       data = g_new0 (DBusOp, 1);
-      data->object = g_object_ref (volume);
+      data->volume = g_object_ref (volume);
       data->callback = callback;
       data->user_data = user_data;
-      data->cancellable = cancellable;
 
+      if (cancellable != NULL)
+        {
+          if (g_cancellable_is_cancelled (cancellable))
+            {
+              GSimpleAsyncResult *simple;
+              simple = g_simple_async_result_new_error (G_OBJECT (volume),
+                                                        callback,
+                                                        user_data,
+                                                        G_IO_ERROR,
+                                                        G_IO_ERROR_CANCELLED,
+                                                        _("The operation was cancelled"));
+              g_simple_async_result_complete_in_idle (simple);
+              g_object_unref (simple);
+              G_UNLOCK (proxy_volume);
+              goto out;
+            }
+
+          data->cancellation_id = g_strdup_printf ("%p", cancellable);
+          data->cancellable = g_object_ref (cancellable);
+          data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                         "cancelled",
+                                                         G_CALLBACK (mount_cancelled),
+                                                         data);
+        }
+      else
+        {
+          data->cancellation_id = g_strdup ("");
+        }
+
+      if (mount_operation != NULL)
+        {
+          data->mount_op_id = g_strdup_printf ("%p", mount_operation);
+          data->mount_operation = g_object_ref (mount_operation);
+          g_hash_table_insert (proxy_volume->hash_mount_op_id_to_data,
+                               data->mount_op_id,
+                               data);
+        }
+      else
+        {
+          data->mount_op_id = g_strdup ("");
+        }
+
       connection = g_proxy_volume_monitor_get_dbus_connection (proxy_volume->volume_monitor);
       name = g_proxy_volume_monitor_get_dbus_name (proxy_volume->volume_monitor);
 
       message = dbus_message_new_method_call (name,
-                                              "/",
+                                              "/org/gtk/Private/RemoteVolumeMonitor",
                                               "org.gtk.Private.RemoteVolumeMonitor",
                                               "VolumeMount");
       dbus_message_append_args (message,
                                 DBUS_TYPE_STRING,
                                 &(proxy_volume->id),
+                                DBUS_TYPE_STRING,
+                                &(data->cancellation_id),
                                 DBUS_TYPE_UINT32,
                                 &_flags,
-                                DBUS_TYPE_BOOLEAN,
-                                &use_mount_operation,
+                                DBUS_TYPE_STRING,
+                                &(data->mount_op_id),
                                 DBUS_TYPE_INVALID);
       G_UNLOCK (proxy_volume);
 
       _g_dbus_connection_call_async (connection,
                                      message,
-                                     -1,
+                                     30 * 60 * 1000,                /* 30 minute timeout */
                                      (GAsyncDBusCallback) mount_cb,
                                      data);
       dbus_message_unref (message);
       dbus_connection_unref (connection);
     }
+
+ out:
+  ;
 }
 
+
+static void
+mount_op_reply_cb (DBusMessage *reply,
+                   GError      *error,
+                   DBusOp      *data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from MountOpReply(): %s", error->message);
+    }
+}
+
+static void
+mount_operation_reply (GMountOperation        *mount_operation,
+                       GMountOperationResult  result,
+                       gpointer               user_data)
+{
+  DBusOp *data = user_data;
+  DBusConnection *connection;
+  const char *name;
+  DBusMessage *message;
+  const char *user_name;
+  const char *domain;
+  const char *password;
+  dbus_uint32_t password_save;
+  dbus_uint32_t choice;
+  dbus_bool_t anonymous;
+
+  connection = g_proxy_volume_monitor_get_dbus_connection (data->volume->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (data->volume->volume_monitor);
+
+  user_name     = g_mount_operation_get_username (mount_operation);
+  domain        = g_mount_operation_get_domain (mount_operation);
+  password      = g_mount_operation_get_password (mount_operation);
+  password_save = g_mount_operation_get_password_save (mount_operation);
+  choice        = g_mount_operation_get_choice (mount_operation);
+  anonymous     = g_mount_operation_get_anonymous (mount_operation);
+
+  if (user_name == NULL)
+    user_name = "";
+  if (domain == NULL)
+    domain = "";
+  if (password == NULL)
+    password = "";
+
+  message = dbus_message_new_method_call (name,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountOpReply");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(data->volume->id),
+                            DBUS_TYPE_STRING,
+                            &(data->mount_op_id),
+                            DBUS_TYPE_INT32,
+                            &result,
+                            DBUS_TYPE_STRING,
+                            &user_name,
+                            DBUS_TYPE_STRING,
+                            &domain,
+                            DBUS_TYPE_STRING,
+                            &password,
+                            DBUS_TYPE_INT32,
+                            &password_save,
+                            DBUS_TYPE_INT32,
+                            &choice,
+                            DBUS_TYPE_BOOLEAN,
+                            &anonymous,
+                            DBUS_TYPE_INVALID);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) mount_op_reply_cb,
+                                 data);
+  dbus_message_unref (message);
+  dbus_connection_unref (connection);
+}
+
+void
+g_proxy_volume_handle_mount_op_ask_password (GProxyVolume        *volume,
+                                             DBusMessageIter     *iter)
+{
+  const char *mount_op_id;
+  const char *message;
+  const char *default_user;
+  const char *default_domain;
+  dbus_int32_t flags;
+  DBusOp *data;
+
+  dbus_message_iter_get_basic (iter, &mount_op_id);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &message);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &default_user);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &default_domain);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &flags);
+  dbus_message_iter_next (iter);
+
+  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
+
+  /* since eavesdropping is enabled on the session bus we get this signal even if it
+   * is for another application; so silently ignore it if it's not for us
+   */
+  if (data == NULL)
+    goto out;
+
+  if (data->reply_handler_id == 0)
+    {
+      data->reply_handler_id = g_signal_connect (data->mount_operation,
+                                                 "reply",
+                                                 G_CALLBACK (mount_operation_reply),
+                                                 data);
+    }
+
+  g_signal_emit_by_name (data->mount_operation,
+                         "ask-password",
+                         message,
+                         default_user,
+                         default_domain,
+                         flags);
+
+ out:
+  ;
+}
+
+void
+g_proxy_volume_handle_mount_op_ask_question (GProxyVolume        *volume,
+                                             DBusMessageIter     *iter)
+{
+  const char *mount_op_id;
+  const char *message;
+  GPtrArray *choices;
+  DBusMessageIter iter_array;
+  DBusOp *data;
+
+  choices = NULL;
+
+  dbus_message_iter_get_basic (iter, &mount_op_id);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &message);
+  dbus_message_iter_next (iter);
+
+  choices = g_ptr_array_new ();
+  dbus_message_iter_recurse (iter, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      const char *choice;
+      dbus_message_iter_get_basic (&iter_array, &choice);
+      dbus_message_iter_next (&iter_array);
+
+      g_ptr_array_add (choices, g_strdup (choice));
+    }
+  g_ptr_array_add (choices, NULL);
+
+  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
+
+  /* since eavesdropping is enabled on the session bus we get this signal even if it
+   * is for another application; so silently ignore it if it's not for us
+   */
+  if (data == NULL)
+    goto out;
+
+  if (data->reply_handler_id == 0)
+    {
+      data->reply_handler_id = g_signal_connect (data->mount_operation,
+                                                 "reply",
+                                                 G_CALLBACK (mount_operation_reply),
+                                                 data);
+    }
+
+  g_signal_emit_by_name (data->mount_operation,
+                         "ask-question",
+                         message,
+                         choices->pdata);
+
+ out:
+  g_ptr_array_free (choices, TRUE);
+}
+
+void
+g_proxy_volume_handle_mount_op_aborted (GProxyVolume        *volume,
+                                        DBusMessageIter     *iter)
+{
+  const char *mount_op_id;
+  DBusOp *data;
+
+  dbus_message_iter_get_basic (iter, &mount_op_id);
+  dbus_message_iter_next (iter);
+
+  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
+
+  /* since eavesdropping is enabled on the session bus we get this signal even if it
+   * is for another application; so silently ignore it if it's not for us
+   */
+  if (data == NULL)
+    goto out;
+
+  g_signal_emit_by_name (data->mount_operation, "aborted");
+
+ out:
+  ;
+}
+
 static gboolean
 g_proxy_volume_mount_finish (GVolume        *volume,
                              GAsyncResult  *result,
Index: monitor/proxy/gproxydrive.c
===================================================================
--- monitor/proxy/gproxydrive.c	(revision 2238)
+++ monitor/proxy/gproxydrive.c	(working copy)
@@ -128,10 +128,11 @@
  * boolean              can-poll-for-media
  * boolean              has-media
  * boolean              is-media-removable
+ * boolean              is-media-check-automatic
  * array:string         volume-ids
  * dict:string->string  identifiers
  */
-#define DRIVE_STRUCT_TYPE "(sssbbbasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbasa{ss})"
 
 void
 g_proxy_drive_update (GProxyDrive         *drive,
@@ -146,6 +147,7 @@
   dbus_bool_t can_poll_for_media;
   dbus_bool_t has_media;
   dbus_bool_t is_media_removable;
+  dbus_bool_t is_media_check_automatic;
   GPtrArray *volume_ids;
   GHashTable *identifiers;
 
@@ -164,6 +166,8 @@
   dbus_message_iter_next (&iter_struct);
   dbus_message_iter_get_basic (&iter_struct, &is_media_removable);
   dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &is_media_check_automatic);
+  dbus_message_iter_next (&iter_struct);
 
   volume_ids = g_ptr_array_new ();
   dbus_message_iter_recurse (&iter_struct, &iter_volume_ids_iter);
@@ -210,6 +214,7 @@
   drive->can_poll_for_media = can_poll_for_media;
   drive->has_media = has_media;
   drive->is_media_removable = is_media_removable;
+  drive->is_media_check_automatic = is_media_check_automatic;
   drive->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL;
   drive->volume_ids = g_strdupv ((char **) volume_ids->pdata);
 
@@ -397,32 +402,102 @@
 }
 
 typedef struct {
-  GObject *object;
+  GProxyDrive *drive;
   GAsyncReadyCallback callback;
   gpointer user_data;
+
+  gboolean is_cancelled;
+  gchar *cancellation_id;
   GCancellable *cancellable;
+  gulong cancelled_handler_id;
 } DBusOp;
 
 static void
+cancel_operation_reply_cb (DBusMessage *reply,
+                           GError      *error,
+                           gpointer     user_data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from CancelOperation(): %s", error->message);
+    }
+}
+
+static void
+operation_cancelled (GCancellable *cancellable,
+                     gpointer      user_data)
+{
+  DBusOp *data = user_data;
+
+  if (!data->is_cancelled)
+    {
+      GSimpleAsyncResult *simple;
+      DBusConnection *connection;
+      DBusMessage *message;
+      const char *name;
+
+      data->is_cancelled = TRUE;
+
+      simple = g_simple_async_result_new_error (G_OBJECT (data->drive),
+                                                data->callback,
+                                                data->user_data,
+                                                G_IO_ERROR,
+                                                G_IO_ERROR_CANCELLED,
+                                                _("The operation was cancelled"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+
+      /* Now tell the remote volume monitor that the op has been cancelled */
+      connection = g_proxy_volume_monitor_get_dbus_connection (data->drive->volume_monitor);
+      name = g_proxy_volume_monitor_get_dbus_name (data->drive->volume_monitor);
+      message = dbus_message_new_method_call (name,
+                                              "/org/gtk/Private/RemoteVolumeMonitor",
+                                              "org.gtk.Private.RemoteVolumeMonitor",
+                                              "CancelOperation");
+      dbus_message_append_args (message,
+                                DBUS_TYPE_STRING,
+                                &(data->cancellation_id),
+                                DBUS_TYPE_INVALID);
+
+      _g_dbus_connection_call_async (connection,
+                                     message,
+                                     -1,
+                                     (GAsyncDBusCallback) cancel_operation_reply_cb,
+                                     NULL);
+      dbus_message_unref (message);
+      dbus_connection_unref (connection);
+    }
+}
+
+static void
 eject_cb (DBusMessage *reply,
           GError *error,
           DBusOp *data)
 {
-  GSimpleAsyncResult *simple;
-  if (error != NULL)
-    simple = g_simple_async_result_new_from_error (data->object,
-                                                   data->callback,
-                                                   data->user_data,
-                                                   error);
-  else
-    simple = g_simple_async_result_new (data->object,
-                                        data->callback,
-                                        data->user_data,
-                                        NULL);
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
+  if (!data->is_cancelled)
+    {
+      GSimpleAsyncResult *simple;
 
-  g_object_unref (data->object);
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->drive),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->drive),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+    }
+
+  g_object_unref (data->drive);
+  g_free (data->cancellation_id);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
   g_free (data);
 }
 
@@ -443,21 +518,51 @@
   G_LOCK (proxy_drive);
 
   data = g_new0 (DBusOp, 1);
-  data->object = g_object_ref (drive);
+  data->drive = g_object_ref (drive);
   data->callback = callback;
   data->user_data = user_data;
-  data->cancellable = cancellable;
 
+  if (cancellable != NULL)
+    {
+      if (g_cancellable_is_cancelled (cancellable))
+        {
+          GSimpleAsyncResult *simple;
+          simple = g_simple_async_result_new_error (G_OBJECT (drive),
+                                                    callback,
+                                                    user_data,
+                                                    G_IO_ERROR,
+                                                    G_IO_ERROR_CANCELLED,
+                                                    _("The operation was cancelled"));
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+          G_UNLOCK (proxy_drive);
+          goto out;
+        }
+
+      data->cancellation_id = g_strdup_printf ("%p", cancellable);
+      data->cancellable = g_object_ref (cancellable);
+      data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                     "cancelled",
+                                                     G_CALLBACK (operation_cancelled),
+                                                     data);
+    }
+  else
+    {
+      data->cancellation_id = g_strdup ("");
+    }
+
   connection = g_proxy_volume_monitor_get_dbus_connection (proxy_drive->volume_monitor);
   name = g_proxy_volume_monitor_get_dbus_name (proxy_drive->volume_monitor);
 
   message = dbus_message_new_method_call (name,
-                                          "/",
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "DriveEject");
   dbus_message_append_args (message,
                             DBUS_TYPE_STRING,
                             &(proxy_drive->id),
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
                             DBUS_TYPE_UINT32,
                             &_flags,
                             DBUS_TYPE_INVALID);
@@ -470,6 +575,8 @@
                                  data);
   dbus_connection_unref (connection);
   dbus_message_unref (message);
+ out:
+  ;
 }
 
 static gboolean
@@ -487,21 +594,30 @@
                    GError *error,
                    DBusOp *data)
 {
-  GSimpleAsyncResult *simple;
-  if (error != NULL)
-    simple = g_simple_async_result_new_from_error (data->object,
-                                                   data->callback,
-                                                   data->user_data,
-                                                   error);
-  else
-    simple = g_simple_async_result_new (data->object,
-                                        data->callback,
-                                        data->user_data,
-                                        NULL);
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
+  if (!data->is_cancelled)
+    {
+      GSimpleAsyncResult *simple;
 
-  g_object_unref (data->object);
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->drive),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->drive),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+    }
+
+  g_object_unref (data->drive);
+  g_free (data->cancellation_id);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
   g_free (data);
 }
 
@@ -520,21 +636,51 @@
   G_LOCK (proxy_drive);
 
   data = g_new0 (DBusOp, 1);
-  data->object = g_object_ref (drive);
+  data->drive = g_object_ref (drive);
   data->callback = callback;
   data->user_data = user_data;
-  data->cancellable = cancellable;
 
+  if (cancellable != NULL)
+    {
+      if (g_cancellable_is_cancelled (cancellable))
+        {
+          GSimpleAsyncResult *simple;
+          simple = g_simple_async_result_new_error (G_OBJECT (drive),
+                                                    callback,
+                                                    user_data,
+                                                    G_IO_ERROR,
+                                                    G_IO_ERROR_CANCELLED,
+                                                    _("The operation was cancelled"));
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+          G_UNLOCK (proxy_drive);
+          goto out;
+        }
+
+      data->cancellation_id = g_strdup_printf ("%p", cancellable);
+      data->cancellable = g_object_ref (cancellable);
+      data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                     "cancelled",
+                                                     G_CALLBACK (operation_cancelled),
+                                                     data);
+    }
+  else
+    {
+      data->cancellation_id = g_strdup ("");
+    }
+
   connection = g_proxy_volume_monitor_get_dbus_connection (proxy_drive->volume_monitor);
   name = g_proxy_volume_monitor_get_dbus_name (proxy_drive->volume_monitor);
 
   message = dbus_message_new_method_call (name,
-                                          "/",
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "DrivePollForMedia");
   dbus_message_append_args (message,
                             DBUS_TYPE_STRING,
                             &(proxy_drive->id),
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
                             DBUS_TYPE_INVALID);
   G_UNLOCK (proxy_drive);
 
@@ -545,6 +691,8 @@
                                  data);
   dbus_connection_unref (connection);
   dbus_message_unref (message);
+ out:
+  ;
 }
 
 static gboolean
Index: monitor/proxy/gproxyvolume.h
===================================================================
--- monitor/proxy/gproxyvolume.h	(revision 2238)
+++ monitor/proxy/gproxyvolume.h	(working copy)
@@ -53,6 +53,15 @@
 
 GProxyShadowMount *g_proxy_volume_get_shadow_mount (GProxyVolume        *volume);
 
+void          g_proxy_volume_handle_mount_op_ask_password (GProxyVolume        *volume,
+                                                           DBusMessageIter     *iter);
+
+void          g_proxy_volume_handle_mount_op_ask_question (GProxyVolume        *volume,
+                                                           DBusMessageIter     *iter);
+
+void          g_proxy_volume_handle_mount_op_aborted      (GProxyVolume        *volume,
+                                                           DBusMessageIter     *iter);
+
 G_END_DECLS
 
 #endif /* __G_PROXY_VOLUME_H__ */
Index: monitor/proxy/gproxyvolumemonitor.c
===================================================================
--- monitor/proxy/gproxyvolumemonitor.c	(revision 2238)
+++ monitor/proxy/gproxyvolumemonitor.c	(working copy)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* gvfs - extensions for gio
  *
- * Copyright (C) 2006-2008 Red Hat, Inc.
+ * Copyright (C) 2006-2009 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -21,22 +21,9 @@
  * Author: David Zeuthen <davidz redhat com>
  */
 
-/*
- * TODO: since we already call IsSupported() at module load time (in
- * order to register the appropriate types) we really just should
- * construct all the volume monitors. This is a good idea because
- *
- *  - instead of calling IsSupported() we just call List()
- *    - e.g. exactly the same IPC overhead
- *    - neglible memory + cpu overhead
- *      - will need to construct them at some point
- *    - we can actually implement get_mount_for_mount_path()
- *      correctly even when no volume monitor is constructed
- *
- *  - implement support for GMountOperation
- *    - not implemented in the HAL volume monitor and that's all
- *      that is using it right now. Will implement at some point
- *      when it's needed or someone has spare cycles.
+/* TODO: handle force_rescan in g_mount_guess_content_type(); right now we
+ *       just scan in the daemon first time the GMount is seen and
+ *       cache that result forever.
  */
 
 #include <config.h>
@@ -68,6 +55,9 @@
   GHashTable *drives;
   GHashTable *volumes;
   GHashTable *mounts;
+
+  /* The unique D-Bus name of the remote monitor or NULL if disconnected */
+  gchar *unique_name;
 };
 
 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyVolumeMonitor,
@@ -107,14 +97,24 @@
 };
 
 static char *
-get_match_rule (GProxyVolumeMonitor *monitor)
+get_match_rule_for_signals (GProxyVolumeMonitor *monitor)
 {
   return g_strdup_printf ("type='signal',"
                           "interface='org.gtk.Private.RemoteVolumeMonitor',"
-                          "sender='%s'",
+                          "sender='%s',",
                           g_proxy_volume_monitor_get_dbus_name (monitor));
 }
 
+static char *
+get_match_rule_for_name_owner_changed (GProxyVolumeMonitor *monitor)
+{
+  return g_strdup_printf ("type='signal',"
+                          "interface='org.freedesktop.DBus',"
+                          "member='NameOwnerChanged',"
+                          "arg0='%s'",
+                          g_proxy_volume_monitor_get_dbus_name (monitor));
+}
+
 static void
 g_proxy_volume_monitor_finalize (GObject *object)
 {
@@ -135,20 +135,32 @@
   g_hash_table_unref (monitor->volumes);
   g_hash_table_unref (monitor->mounts);
 
+  g_free (monitor->unique_name);
+
   dbus_connection_remove_filter (monitor->session_bus, filter_function, monitor);
-  match_rule = get_match_rule (monitor);
+
+  match_rule = get_match_rule_for_signals (monitor);
   dbus_error_init (&dbus_error);
   dbus_bus_remove_match (monitor->session_bus,
                          match_rule,
                          &dbus_error);
   if (dbus_error_is_set (&dbus_error)) {
-    /* not really a whole lot to do on failure than whine since
-     * GObject finalization can't fail...
-     */
     g_warning ("cannot remove match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
     dbus_error_free (&dbus_error);
   }
   g_free (match_rule);
+
+  match_rule = get_match_rule_for_name_owner_changed (monitor);
+  dbus_error_init (&dbus_error);
+  dbus_bus_remove_match (monitor->session_bus,
+                         match_rule,
+                         &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    g_warning ("cannot remove match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
+    dbus_error_free (&dbus_error);
+  }
+  g_free (match_rule);
+
   dbus_connection_unref (monitor->session_bus);
 
   if (parent_class->finalize)
@@ -306,16 +318,35 @@
   GHashTableIter vm_hash_iter;
   GHashTableIter vol_hash_iter;
   GProxyMount *candidate_mount;
+  static GVolumeMonitor *union_monitor = NULL;
 
-  /* This static method on GNativeVolumeMonitor is a *complete* pain
-   * in the ass to deal with; we need to rework gio so it's deprecated
-   * and thus never will get called.
+  /* There's a problem here insofar that this static method on GNativeVolumeMonitor can
+   * be called *before* any of our monitors are constructed. Since this method doesn't
+   * pass in the class structure we *know* which native remote monitor to use.
    *
-   * TODO: we don't handle the case when there's no volume monitor ever
-   * constructed. See TODO at the top of this file on how to handle that.
+   * To work around that, we get the singleton GVolumeMonitor... This will trigger
+   * construction of a GUnionVolumeMonitor in gio which will construct the *appropriate*
+   * remote volume monitors to use (it's up to gio to pick which one to use).
+   *
+   * Note that we will *hold* on to this reference effectively making us a resident
+   * module. And effectively keeping volume monitoring alive.
+   *
+   * The reason we hold on to the reference is that otherwise we'd be constructing/destructing
+   * *all* proxy volume monitors (which includes synchronous D-Bus calls to seed the monitor)
+   * every time this method is called.
+   *
+   * Note that *simple* GIO apps that a) don't use volume monitors; and b) don't use the
+   * g_file_find_enclosing_mount() method will never see any volume monitor overhead.
    */
 
+  /* Note that g_volume_monitor_get() is thread safe. We don't want to call it while
+   * holding the proxy_vm lock since it might end up calling our constructor.
+   */
+  if (union_monitor == NULL)
+    union_monitor = g_volume_monitor_get ();
+
   mount = NULL;
+
   G_LOCK (proxy_vm);
 
   /* First find the native volume monitor if one exists */
@@ -390,19 +421,29 @@
   monitor->mounts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 
   dbus_connection_add_filter (monitor->session_bus, filter_function, monitor, NULL);
-  match_rule = get_match_rule (monitor);
+
+  /* listen to volume monitor signals */
+  match_rule = get_match_rule_for_signals (monitor);
   dbus_bus_add_match (monitor->session_bus,
                       match_rule,
                       &dbus_error);
   if (dbus_error_is_set (&dbus_error)) {
-    /* not really a whole lot to do on failure than whine since
-     * GObject construction can't fail...
-     */
     g_warning ("cannot add match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
     dbus_error_free (&dbus_error);
   }
   g_free (match_rule);
 
+  /* listen to when the owner of the service appears/disappears */
+  match_rule = get_match_rule_for_name_owner_changed (monitor);
+  dbus_bus_add_match (monitor->session_bus,
+                      match_rule,
+                      &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    g_warning ("cannot add match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
+    dbus_error_free (&dbus_error);
+  }
+  g_free (match_rule);
+
   seed_monitor (monitor);
 
   g_hash_table_insert (the_volume_monitors, (gpointer) type, object);
@@ -470,179 +511,284 @@
 
   member = dbus_message_get_member (message);
 
-  if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveChanged") ||
-      dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveConnected") ||
-      dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveDisconnected") ||
-      dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEjectButton")) {
+  if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameOwnerChanged"))
+    {
+      GHashTableIter hash_iter;
+      GProxyMount *mount;
+      GProxyVolume *volume;
+      GProxyDrive *drive;
+      const gchar *name;
+      const gchar *old_owner;
+      const gchar *new_owner;
 
-    dbus_message_iter_init (message, &iter);
-    dbus_message_iter_get_basic (&iter, &the_dbus_name);
-    dbus_message_iter_next (&iter);
-    dbus_message_iter_get_basic (&iter, &id);
-    dbus_message_iter_next (&iter);
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &old_owner);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &new_owner);
+      dbus_message_iter_next (&iter);
 
-    if (strcmp (the_dbus_name, klass->dbus_name) != 0)
-      goto not_for_us;
+      if (strcmp (name, klass->dbus_name) != 0)
+        goto not_for_us;
 
-    if (strcmp (member, "DriveChanged") == 0)
-      {
-        drive = g_hash_table_lookup (monitor->drives, id);
-        if (drive != NULL)
-          {
-            g_proxy_drive_update (drive, &iter);
-            signal_emit_in_idle (drive, "changed", NULL);
-            signal_emit_in_idle (monitor, "drive-changed", drive);
-          }
-      }
-    else if (strcmp (member, "DriveConnected") == 0)
-      {
-        drive = g_hash_table_lookup (monitor->drives, id);
-        if (drive == NULL)
-          {
-            drive = g_proxy_drive_new (monitor);
-            g_proxy_drive_update (drive, &iter);
-            g_hash_table_insert (monitor->drives, g_strdup (g_proxy_drive_get_id (drive)), drive);
-            signal_emit_in_idle (monitor, "drive-connected", drive);
-          }
-      }
-    else if (strcmp (member, "DriveDisconnected") == 0)
-      {
-        drive = g_hash_table_lookup (monitor->drives, id);
-        if (drive != NULL)
-          {
-            g_object_ref (drive);
-            g_hash_table_remove (monitor->drives, id);
-            signal_emit_in_idle (drive, "disconnected", NULL);
-            signal_emit_in_idle (monitor, "drive-disconnected", drive);
-            g_object_unref (drive);
-          }
-      }
-    else if (strcmp (member, "DriveEjectButton") == 0)
-      {
-        drive = g_hash_table_lookup (monitor->drives, id);
-        if (drive != NULL)
-          {
-            signal_emit_in_idle (drive, "eject-button", NULL);
-            signal_emit_in_idle (monitor, "drive-eject-button", drive);
-          }
-      }
+      if (monitor->unique_name != NULL && g_strcmp0 (new_owner, monitor->unique_name) != 0)
+        {
+          g_warning ("Owner %s of volume monitor %s disconnected from the bus; removing drives/volumes/mounts",
+                     monitor->unique_name,
+                     klass->dbus_name);
 
-  } else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeChanged") ||
-             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeAdded") ||
-             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeRemoved")) {
+          g_hash_table_iter_init (&hash_iter, monitor->mounts);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &mount))
+            {
+              signal_emit_in_idle (mount, "unmounted", NULL);
+              signal_emit_in_idle (monitor, "mount-removed", mount);
+            }
+          g_hash_table_remove_all (monitor->mounts);
 
-    dbus_message_iter_init (message, &iter);
-    dbus_message_iter_get_basic (&iter, &the_dbus_name);
-    dbus_message_iter_next (&iter);
-    dbus_message_iter_get_basic (&iter, &id);
-    dbus_message_iter_next (&iter);
+          g_hash_table_iter_init (&hash_iter, monitor->volumes);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &volume))
+            {
+              signal_emit_in_idle (volume, "removed", NULL);
+              signal_emit_in_idle (monitor, "volume-removed", volume);
+            }
+          g_hash_table_remove_all (monitor->volumes);
 
-    if (strcmp (the_dbus_name, klass->dbus_name) != 0)
-      goto not_for_us;
+          g_hash_table_iter_init (&hash_iter, monitor->drives);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &drive))
+            {
+              signal_emit_in_idle (drive, "disconnected", NULL);
+              signal_emit_in_idle (monitor, "drive-disconnected", drive);
+            }
+          g_hash_table_remove_all (monitor->drives);
 
-    if (strcmp (member, "VolumeChanged") == 0)
-      {
-        volume = g_hash_table_lookup (monitor->volumes, id);
-        if (volume != NULL)
-          {
-            GProxyShadowMount *shadow_mount;
+          g_free (monitor->unique_name);
+          monitor->unique_name = NULL;
 
-            g_proxy_volume_update (volume, &iter);
-            signal_emit_in_idle (volume, "changed", NULL);
-            signal_emit_in_idle (monitor, "volume-changed", volume);
+          /* TODO: maybe try to relaunch the monitor? */
 
-            shadow_mount = g_proxy_volume_get_shadow_mount (volume);
-            if (shadow_mount != NULL)
-              {
-                signal_emit_in_idle (shadow_mount, "changed", NULL);
-                signal_emit_in_idle (monitor, "mount-changed", shadow_mount);
-                g_object_unref (shadow_mount);
-              }
-          }
-      }
-    else if (strcmp (member, "VolumeAdded") == 0)
-      {
-        volume = g_hash_table_lookup (monitor->volumes, id);
-        if (volume == NULL)
-          {
-            volume = g_proxy_volume_new (monitor);
-            g_proxy_volume_update (volume, &iter);
-            g_hash_table_insert (monitor->volumes, g_strdup (g_proxy_volume_get_id (volume)), volume);
-            signal_emit_in_idle (monitor, "volume-added", volume);
-          }
-      }
-    else if (strcmp (member, "VolumeRemoved") == 0)
-      {
-        volume = g_hash_table_lookup (monitor->volumes, id);
-        if (volume != NULL)
-          {
-            g_object_ref (volume);
-            g_hash_table_remove (monitor->volumes, id);
-            signal_emit_in_idle (volume, "removed", NULL);
-            signal_emit_in_idle (monitor, "volume-removed", volume);
-            g_object_unref (volume);
-          }
-      }
+        }
 
-  } else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountChanged") ||
-             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountAdded") ||
-             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountPreUnmount") ||
-             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountRemoved")) {
+      if (strlen (new_owner) > 0 && monitor->unique_name == NULL)
+        {
+          g_warning ("New owner %s for volume monitor %s connected to the bus; seeding drives/volumes/mounts",
+                     new_owner,
+                     klass->dbus_name);
 
-    dbus_message_iter_init (message, &iter);
-    dbus_message_iter_get_basic (&iter, &the_dbus_name);
-    dbus_message_iter_next (&iter);
-    dbus_message_iter_get_basic (&iter, &id);
-    dbus_message_iter_next (&iter);
+          seed_monitor (monitor);
 
-    if (strcmp (the_dbus_name, klass->dbus_name) != 0)
-      goto not_for_us;
+          /* emit signals for all the drives/volumes/mounts "added" */
+          g_hash_table_iter_init (&hash_iter, monitor->drives);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &drive))
+            signal_emit_in_idle (monitor, "drive-connected", drive);
 
-    if (strcmp (member, "MountChanged") == 0)
-      {
-        mount = g_hash_table_lookup (monitor->mounts, id);
-        if (mount != NULL)
-          {
-            g_proxy_mount_update (mount, &iter);
-            signal_emit_in_idle (mount, "changed", NULL);
-            signal_emit_in_idle (monitor, "mount-changed", mount);
-          }
-      }
-    else if (strcmp (member, "MountAdded") == 0)
-      {
-        mount = g_hash_table_lookup (monitor->mounts, id);
-        if (mount == NULL)
-          {
-            mount = g_proxy_mount_new (monitor);
-            g_proxy_mount_update (mount, &iter);
-            g_hash_table_insert (monitor->mounts, g_strdup (g_proxy_mount_get_id (mount)), mount);
+          g_hash_table_iter_init (&hash_iter, monitor->volumes);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &volume))
+            signal_emit_in_idle (monitor, "volume-added", volume);
+
+          g_hash_table_iter_init (&hash_iter, monitor->mounts);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &mount))
             signal_emit_in_idle (monitor, "mount-added", mount);
-          }
-      }
-    else if (strcmp (member, "MountPreUnmount") == 0)
-      {
-        mount = g_hash_table_lookup (monitor->mounts, id);
-        if (mount != NULL)
-          {
-            signal_emit_in_idle (mount, "pre-unmount", NULL);
-            signal_emit_in_idle (monitor, "mount-pre-unmount", mount);
-          }
-      }
-    else if (strcmp (member, "MountRemoved") == 0)
-      {
-        mount = g_hash_table_lookup (monitor->mounts, id);
-        if (mount != NULL)
-          {
-            g_object_ref (mount);
-            g_hash_table_remove (monitor->mounts, id);
-            signal_emit_in_idle (mount, "unmounted", NULL);
-            signal_emit_in_idle (monitor, "mount-removed", mount);
-            g_object_unref (mount);
-          }
-      }
+        }
 
-  }
+    }
+  else  if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveChanged") ||
+            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveConnected") ||
+            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveDisconnected") ||
+            dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEjectButton"))
+    {
 
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &the_dbus_name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &id);
+      dbus_message_iter_next (&iter);
+
+      if (strcmp (the_dbus_name, klass->dbus_name) != 0)
+        goto not_for_us;
+
+      if (strcmp (member, "DriveChanged") == 0)
+        {
+          drive = g_hash_table_lookup (monitor->drives, id);
+          if (drive != NULL)
+            {
+              g_proxy_drive_update (drive, &iter);
+              signal_emit_in_idle (drive, "changed", NULL);
+              signal_emit_in_idle (monitor, "drive-changed", drive);
+            }
+        }
+      else if (strcmp (member, "DriveConnected") == 0)
+        {
+          drive = g_hash_table_lookup (monitor->drives, id);
+          if (drive == NULL)
+            {
+              drive = g_proxy_drive_new (monitor);
+              g_proxy_drive_update (drive, &iter);
+              g_hash_table_insert (monitor->drives, g_strdup (g_proxy_drive_get_id (drive)), drive);
+              signal_emit_in_idle (monitor, "drive-connected", drive);
+            }
+        }
+      else if (strcmp (member, "DriveDisconnected") == 0)
+        {
+          drive = g_hash_table_lookup (monitor->drives, id);
+          if (drive != NULL)
+            {
+              g_object_ref (drive);
+              g_hash_table_remove (monitor->drives, id);
+              signal_emit_in_idle (drive, "disconnected", NULL);
+              signal_emit_in_idle (monitor, "drive-disconnected", drive);
+              g_object_unref (drive);
+            }
+        }
+      else if (strcmp (member, "DriveEjectButton") == 0)
+        {
+          drive = g_hash_table_lookup (monitor->drives, id);
+          if (drive != NULL)
+            {
+              signal_emit_in_idle (drive, "eject-button", NULL);
+              signal_emit_in_idle (monitor, "drive-eject-button", drive);
+            }
+        }
+
+    }
+  else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeChanged") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeAdded") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeRemoved") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskPassword") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskQuestion") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAborted"))
+    {
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &the_dbus_name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &id);
+      dbus_message_iter_next (&iter);
+
+      if (strcmp (the_dbus_name, klass->dbus_name) != 0)
+        goto not_for_us;
+
+      if (strcmp (member, "VolumeChanged") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            {
+              GProxyShadowMount *shadow_mount;
+
+              g_proxy_volume_update (volume, &iter);
+              signal_emit_in_idle (volume, "changed", NULL);
+              signal_emit_in_idle (monitor, "volume-changed", volume);
+
+              shadow_mount = g_proxy_volume_get_shadow_mount (volume);
+              if (shadow_mount != NULL)
+                {
+                  signal_emit_in_idle (shadow_mount, "changed", NULL);
+                  signal_emit_in_idle (monitor, "mount-changed", shadow_mount);
+                  g_object_unref (shadow_mount);
+                }
+            }
+        }
+      else if (strcmp (member, "VolumeAdded") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume == NULL)
+            {
+              volume = g_proxy_volume_new (monitor);
+              g_proxy_volume_update (volume, &iter);
+              g_hash_table_insert (monitor->volumes, g_strdup (g_proxy_volume_get_id (volume)), volume);
+              signal_emit_in_idle (monitor, "volume-added", volume);
+            }
+        }
+      else if (strcmp (member, "VolumeRemoved") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            {
+              g_object_ref (volume);
+              g_hash_table_remove (monitor->volumes, id);
+              signal_emit_in_idle (volume, "removed", NULL);
+              signal_emit_in_idle (monitor, "volume-removed", volume);
+              g_object_unref (volume);
+            }
+        }
+      else if (strcmp (member, "MountOpAskPassword") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            g_proxy_volume_handle_mount_op_ask_password (volume, &iter);
+        }
+      else if (strcmp (member, "MountOpAskQuestion") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            g_proxy_volume_handle_mount_op_ask_question (volume, &iter);
+        }
+      else if (strcmp (member, "MountOpAborted") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            g_proxy_volume_handle_mount_op_aborted (volume, &iter);
+        }
+
+    }
+  else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountChanged") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountAdded") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountPreUnmount") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountRemoved"))
+    {
+
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &the_dbus_name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &id);
+      dbus_message_iter_next (&iter);
+
+      if (strcmp (the_dbus_name, klass->dbus_name) != 0)
+        goto not_for_us;
+
+      if (strcmp (member, "MountChanged") == 0)
+        {
+          mount = g_hash_table_lookup (monitor->mounts, id);
+          if (mount != NULL)
+            {
+              g_proxy_mount_update (mount, &iter);
+              signal_emit_in_idle (mount, "changed", NULL);
+              signal_emit_in_idle (monitor, "mount-changed", mount);
+            }
+        }
+      else if (strcmp (member, "MountAdded") == 0)
+        {
+          mount = g_hash_table_lookup (monitor->mounts, id);
+          if (mount == NULL)
+            {
+              mount = g_proxy_mount_new (monitor);
+              g_proxy_mount_update (mount, &iter);
+              g_hash_table_insert (monitor->mounts, g_strdup (g_proxy_mount_get_id (mount)), mount);
+              signal_emit_in_idle (monitor, "mount-added", mount);
+            }
+        }
+      else if (strcmp (member, "MountPreUnmount") == 0)
+        {
+          mount = g_hash_table_lookup (monitor->mounts, id);
+          if (mount != NULL)
+            {
+              signal_emit_in_idle (mount, "pre-unmount", NULL);
+              signal_emit_in_idle (monitor, "mount-pre-unmount", mount);
+            }
+        }
+      else if (strcmp (member, "MountRemoved") == 0)
+        {
+          mount = g_hash_table_lookup (monitor->mounts, id);
+          if (mount != NULL)
+            {
+              g_object_ref (mount);
+              g_hash_table_remove (monitor->mounts, id);
+              signal_emit_in_idle (mount, "unmounted", NULL);
+              signal_emit_in_idle (monitor, "mount-removed", mount);
+              g_object_unref (mount);
+            }
+        }
+    }
+
  not_for_us:
   G_UNLOCK (proxy_vm);
 
@@ -706,7 +852,7 @@
   is_supported = FALSE;
 
   message = dbus_message_new_method_call (dbus_name,
-                                          "/",
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "IsSupported");
   if (message == NULL)
@@ -788,7 +934,7 @@
   native_class->get_mount_for_mount_path = get_mount_for_mount_path;
 }
 
-/* Must be called with no locks held */
+/* Call with proxy_vm lock held */
 static void
 seed_monitor (GProxyVolumeMonitor *monitor)
 {
@@ -799,7 +945,7 @@
   DBusMessageIter iter_array;
 
   message = dbus_message_new_method_call (g_proxy_volume_monitor_get_dbus_name (monitor),
-                                          "/",
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "List");
   if (message == NULL)
@@ -869,6 +1015,8 @@
     }
   dbus_message_iter_next (&iter_reply);
 
+  monitor->unique_name = g_strdup (dbus_message_get_sender (reply));
+
   dbus_message_unref (reply);
 
  fail:
Index: monitor/proxy/remote-volume-monitor-module.c
===================================================================
--- monitor/proxy/remote-volume-monitor-module.c	(revision 2238)
+++ monitor/proxy/remote-volume-monitor-module.c	(working copy)
@@ -41,6 +41,15 @@
   if (g_getenv ("GVFS_REMOTE_VOLUME_MONITOR_IGNORE") != NULL)
     goto out;
 
+  /* We make this module resident since we *may* hold on to an instance
+   * of the union monitor in the static method get_mount_for_mount_path()
+   * on GNativeVolumeMonitor. And it doesn't make much sense to unload
+   * the module *anyway*.
+   *
+   * See the comment gproxyvolumemonitor.c:get_mount_for_mount_path().
+   */
+  g_type_module_use (G_TYPE_MODULE (module));
+
   bindtextdomain (GETTEXT_PACKAGE, GVFS_LOCALEDIR);
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
Index: monitor/proxy/gproxymount.c
===================================================================
--- monitor/proxy/gproxymount.c	(revision 2238)
+++ monitor/proxy/gproxymount.c	(working copy)
@@ -415,32 +415,101 @@
 }
 
 typedef struct {
-  GObject *object;
+  GProxyMount *mount;
   GAsyncReadyCallback callback;
   gpointer user_data;
+
+  gboolean is_cancelled;
+  gchar *cancellation_id;
   GCancellable *cancellable;
+  gulong cancelled_handler_id;
 } DBusOp;
 
 static void
+cancel_operation_reply_cb (DBusMessage *reply,
+                           GError      *error,
+                           gpointer     user_data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from CancelOperation(): %s", error->message);
+    }
+}
+
+static void
+operation_cancelled (GCancellable *cancellable,
+                     gpointer      user_data)
+{
+  DBusOp *data = user_data;
+
+  if (!data->is_cancelled)
+    {
+      GSimpleAsyncResult *simple;
+      DBusConnection *connection;
+      DBusMessage *message;
+      const char *name;
+
+      data->is_cancelled = TRUE;
+
+      simple = g_simple_async_result_new_error (G_OBJECT (data->mount),
+                                                data->callback,
+                                                data->user_data,
+                                                G_IO_ERROR,
+                                                G_IO_ERROR_CANCELLED,
+                                                _("The operation was cancelled"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+
+      /* Now tell the remote volume monitor that the op has been cancelled */
+      connection = g_proxy_volume_monitor_get_dbus_connection (data->mount->volume_monitor);
+      name = g_proxy_volume_monitor_get_dbus_name (data->mount->volume_monitor);
+      message = dbus_message_new_method_call (name,
+                                              "/org/gtk/Private/RemoteVolumeMonitor",
+                                              "org.gtk.Private.RemoteVolumeMonitor",
+                                              "CancelOperation");
+      dbus_message_append_args (message,
+                                DBUS_TYPE_STRING,
+                                &(data->cancellation_id),
+                                DBUS_TYPE_INVALID);
+
+      _g_dbus_connection_call_async (connection,
+                                     message,
+                                     -1,
+                                     (GAsyncDBusCallback) cancel_operation_reply_cb,
+                                     NULL);
+      dbus_message_unref (message);
+      dbus_connection_unref (connection);
+    }
+}
+
+static void
 unmount_cb (DBusMessage *reply,
             GError *error,
             DBusOp *data)
 {
-  GSimpleAsyncResult *simple;
-  if (error != NULL)
-    simple = g_simple_async_result_new_from_error (data->object,
-                                                   data->callback,
-                                                   data->user_data,
-                                                   error);
-  else
-    simple = g_simple_async_result_new (data->object,
-                                        data->callback,
-                                        data->user_data,
-                                        NULL);
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
+  if (!data->is_cancelled)
+    {
+      GSimpleAsyncResult *simple;
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->mount),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+    }
 
-  g_object_unref (data->object);
+  g_object_unref (data->mount);
+  g_free (data->cancellation_id);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
   g_free (data);
 }
 
@@ -461,21 +530,51 @@
   G_LOCK (proxy_mount);
 
   data = g_new0 (DBusOp, 1);
-  data->object = g_object_ref (mount);
+  data->mount = g_object_ref (mount);
   data->callback = callback;
   data->user_data = user_data;
-  data->cancellable = cancellable;
 
+  if (cancellable != NULL)
+    {
+      if (g_cancellable_is_cancelled (cancellable))
+        {
+          GSimpleAsyncResult *simple;
+          simple = g_simple_async_result_new_error (G_OBJECT (mount),
+                                                    callback,
+                                                    user_data,
+                                                    G_IO_ERROR,
+                                                    G_IO_ERROR_CANCELLED,
+                                                    _("The operation was cancelled"));
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+          G_UNLOCK (proxy_mount);
+          goto out;
+        }
+
+      data->cancellation_id = g_strdup_printf ("%p", cancellable);
+      data->cancellable = g_object_ref (cancellable);
+      data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                     "cancelled",
+                                                     G_CALLBACK (operation_cancelled),
+                                                     data);
+    }
+  else
+    {
+      data->cancellation_id = g_strdup ("");
+    }
+
   connection = g_proxy_volume_monitor_get_dbus_connection (proxy_mount->volume_monitor);
   name = g_proxy_volume_monitor_get_dbus_name (proxy_mount->volume_monitor);
 
   message = dbus_message_new_method_call (name,
-                                          "/",
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "MountUnmount");
   dbus_message_append_args (message,
                             DBUS_TYPE_STRING,
                             &(proxy_mount->id),
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
                             DBUS_TYPE_UINT32,
                             &_flags,
                             DBUS_TYPE_INVALID);
@@ -489,6 +588,8 @@
 
   dbus_message_unref (message);
   dbus_connection_unref (connection);
+ out:
+  ;
 }
 
 static gboolean
Index: monitor/proxy/gvfsproxyvolumemonitordaemon.c
===================================================================
--- monitor/proxy/gvfsproxyvolumemonitordaemon.c	(revision 2238)
+++ monitor/proxy/gvfsproxyvolumemonitordaemon.c	(working copy)
@@ -28,15 +28,173 @@
 #include <dbus/dbus.h>
 #include <glib/gi18n.h>
 #include <stdlib.h>
+#include <glib/gprintf.h>
 
 #include "gdbusutils.h"
 #include "gvfsproxyvolumemonitordaemon.h"
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* #define DEBUG_ENABLED */
+
+#ifdef DEBUG_ENABLED
+static void
+print_debug (const gchar *format, ...)
+{
+  va_list      var_args;
+
+  va_start (var_args, format);
+
+  g_print ("### debug: ");
+  g_vprintf (format, var_args);
+  g_print ("\n");
+
+  va_end (var_args);
+}
+#else
+static void
+print_debug (const gchar *format, ...)
+{
+}
+#endif
+
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GType g_proxy_mount_operation_get_type (void) G_GNUC_CONST;
+
+typedef struct
+{
+  GMountOperation parent_instance;
+} GProxyMountOperation;
+
+typedef struct
+{
+  GMountOperationClass parent_class;
+} GProxyMountOperationClass;
+
+
+static GMountOperation *
+g_proxy_mount_operation_new (void)
+{
+  return G_MOUNT_OPERATION (g_object_new (g_proxy_mount_operation_get_type(), NULL));
+}
+
+G_DEFINE_TYPE (GProxyMountOperation, g_proxy_mount_operation, G_TYPE_MOUNT_OPERATION)
+
+static void
+g_proxy_mount_operation_init (GProxyMountOperation *mount_operation)
+{
+}
+
+static void
+g_proxy_mount_operation_ask_password (GMountOperation *op,
+                                      const char      *message,
+                                      const char      *default_user,
+                                      const char      *default_domain,
+                                      GAskPasswordFlags flags)
+{
+  /* do nothing */
+}
+
+static void
+g_proxy_mount_operation_ask_question (GMountOperation *op,
+                                      const char      *message,
+                                      const char      *choices[])
+{
+  /* do nothing */
+}
+
+static void
+g_proxy_mount_operation_class_init (GProxyMountOperationClass *klass)
+{
+  GMountOperationClass *mount_op_class;
+
+  mount_op_class = G_MOUNT_OPERATION_CLASS (klass);
+
+  mount_op_class->ask_password = g_proxy_mount_operation_ask_password;
+  mount_op_class->ask_question = g_proxy_mount_operation_ask_question;
+}
+
+
 static GVolumeMonitor *monitor = NULL;
 static DBusConnection *connection = NULL;
 static GType the_volume_monitor_type;
 static const char *the_dbus_name = NULL;
 
+static GList *outstanding_ops = NULL;
+
+static GHashTable *unique_names_being_watched = NULL;
+
+static void
+cancellable_destroyed_cb (gpointer user_data,
+                          GObject *where_the_cancellable_was)
+{
+  outstanding_ops = g_list_remove (outstanding_ops, where_the_cancellable_was);
+}
+
+static void
+remove_name_owned_changed_for_unique_name (const gchar *unique_name)
+{
+  const gchar *match_rule;
+  DBusError dbus_error;
+
+  match_rule = g_hash_table_lookup (unique_names_being_watched, unique_name);
+  if (match_rule == NULL)
+    {
+      g_warning ("Was asked to remove match rule for unique_name %s but we don't have one", unique_name);
+      goto out;
+    }
+
+  dbus_error_init (&dbus_error);
+  dbus_bus_remove_match (connection,
+                         match_rule,
+                         &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    g_warning ("cannot remove match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
+    dbus_error_free (&dbus_error);
+  }
+
+  g_hash_table_remove (unique_names_being_watched, unique_name);
+
+ out:
+  ;
+}
+
+static void
+ensure_name_owner_changed_for_unique_name (const gchar *unique_name)
+{
+  gchar *match_rule;
+  DBusError dbus_error;
+
+  if (g_hash_table_lookup (unique_names_being_watched, unique_name) != NULL)
+    goto out;
+
+  match_rule = g_strdup_printf ("type='signal',"
+                                "interface='org.freedesktop.DBus',"
+                                "member='NameOwnerChanged',"
+                                "arg0='%s'",
+                                unique_name);
+
+  dbus_error_init (&dbus_error);
+  dbus_bus_add_match (connection,
+                      match_rule,
+                      &dbus_error);
+  if (dbus_error_is_set (&dbus_error))
+    {
+      g_warning ("cannot add match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      g_free (match_rule);
+      goto out;
+    }
+
+  g_hash_table_insert (unique_names_being_watched, g_strdup (unique_name), match_rule);
+
+ out:
+  ;
+}
+
 static void monitor_try_create (void);
 
 /* string               id
@@ -46,10 +204,11 @@
  * boolean              can-poll-for-media
  * boolean              has-media
  * boolean              is-media-removable
+ * boolean              is-media-check-automatic
  * array:string         volume-ids
  * dict:string->string  identifiers
  */
-#define DRIVE_STRUCT_TYPE "(sssbbbbasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbbasa{ss})"
 
 static void
 append_drive (GDrive *drive, DBusMessageIter *iter_array)
@@ -65,6 +224,7 @@
   gboolean can_poll_for_media;
   gboolean has_media;
   gboolean is_media_removable;
+  gboolean is_media_check_automatic;
   GList *volumes, *l;
   char **identifiers;
   int n;
@@ -82,6 +242,7 @@
   can_poll_for_media = g_drive_can_poll_for_media (drive);
   has_media = g_drive_has_media (drive);
   is_media_removable = g_drive_is_media_removable (drive);
+  is_media_check_automatic = g_drive_is_media_check_automatic (drive);
   volumes = g_drive_get_volumes (drive);
   identifiers = g_drive_enumerate_identifiers (drive);
 
@@ -95,6 +256,7 @@
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_poll_for_media);
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &has_media);
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &is_media_removable);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &is_media_check_automatic);
 
   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_volume_array);
   for (l = volumes; l != NULL; l = l->next)
@@ -108,7 +270,7 @@
   dbus_message_iter_close_container (&iter_struct, &iter_volume_array);
 
   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{ss}", &iter_identifiers);
-  for (n = 0; identifiers[n] != NULL; n++)
+  for (n = 0; identifiers != NULL && identifiers[n] != NULL; n++)
     {
       DBusMessageIter iter_dict_entry;
       char *id_value;
@@ -214,7 +376,7 @@
   dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &mount_id);
 
   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{ss}", &iter_identifiers);
-  for (n = 0; identifiers[n] != NULL; n++)
+  for (n = 0; identifiers != NULL && identifiers[n] != NULL; n++)
     {
       DBusMessageIter iter_dict_entry;
       char *id_value;
@@ -343,6 +505,8 @@
   DBusMessageIter iter_array;
   DBusMessage *reply;
 
+  print_debug ("in handle_list");
+
   drives = g_volume_monitor_get_connected_drives (monitor);
   volumes = g_volume_monitor_get_volumes (monitor);
   mounts = g_volume_monitor_get_mounts (monitor);
@@ -383,14 +547,20 @@
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in mount_unmount_cb");
+
+  g_object_set_data (G_OBJECT (mount), "cancellable", NULL);
+
   error = NULL;
   if (!g_mount_unmount_finish (mount, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -403,6 +573,9 @@
 handle_mount_unmount (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
+  GCancellable *cancellable;
   dbus_uint32_t unmount_flags;
   DBusError dbus_error;
   GList *mounts, *l;
@@ -416,7 +589,8 @@
   dbus_error_init (&dbus_error);
   if (!dbus_message_get_args (message, &dbus_error,
                               DBUS_TYPE_STRING, &id,
-                              DBUS_TYPE_UINT32 &unmount_flags,
+                              DBUS_TYPE_STRING, &cancellation_id,
+                              DBUS_TYPE_UINT32, &unmount_flags,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for MountUnmount(): %s: %s", dbus_error.name, dbus_error.message);
@@ -424,8 +598,12 @@
       goto out;
     }
 
+  print_debug ("in handle_mount_unmount");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   mount = NULL;
   mounts = g_volume_monitor_get_mounts (monitor);
   for (l = mounts; l != NULL; l = l->next)
@@ -453,6 +631,26 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (mount), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (mount), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
+
   g_mount_unmount (mount,
                    unmount_flags,
                    NULL,
@@ -470,20 +668,134 @@
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static DBusHandlerResult
+handle_mount_op_reply (DBusConnection *connection, DBusMessage *message)
+{
+  const char *id;
+  const char *mount_op_id;
+  dbus_int32_t result;
+  const char *user_name;
+  const char *domain;
+  const char *password;
+  dbus_int32_t password_save;
+  dbus_int32_t choice;
+  dbus_bool_t anonymous;
+  DBusError dbus_error;
+  DBusHandlerResult ret;
+  GList *volumes, *l;
+  GVolume *volume;
+  DBusMessage *reply;
+  GMountOperation *mount_operation;
+
+  volumes = NULL;
+  ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message, &dbus_error,
+                              DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_STRING, &mount_op_id,
+                              DBUS_TYPE_INT32, &result,
+                              DBUS_TYPE_STRING, &user_name,
+                              DBUS_TYPE_STRING, &domain,
+                              DBUS_TYPE_STRING, &password,
+                              DBUS_TYPE_INT32, &password_save,
+                              DBUS_TYPE_INT32, &choice,
+                              DBUS_TYPE_BOOLEAN, &anonymous,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for MountOpReply(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  print_debug ("in handle_mount_op_reply");
+
+  ret = DBUS_HANDLER_RESULT_HANDLED;
+
+  volume = NULL;
+  volumes = g_volume_monitor_get_volumes (monitor);
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      char *volume_id;
+
+      volume = G_VOLUME (l->data);
+      volume_id = g_strdup_printf ("%p", volume);
+      if (strcmp (volume_id, id) == 0)
+        break;
+
+      g_free (volume_id);
+    }
+  if (l == NULL)
+    volume = NULL;
+
+  if (volume == NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+                                      "The given volume was not found");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  mount_operation = g_object_get_data (G_OBJECT (volume), "mount_operation");
+  if (mount_operation == NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+                                      "No outstanding mount operation");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  g_mount_operation_set_username (mount_operation, user_name);
+  g_mount_operation_set_domain (mount_operation, domain);
+  g_mount_operation_set_password (mount_operation, password);
+  g_mount_operation_set_password_save (mount_operation, password_save);
+  g_mount_operation_set_choice (mount_operation, choice);
+  g_mount_operation_set_anonymous (mount_operation, anonymous);
+
+  g_mount_operation_reply (mount_operation, result);
+
+  reply = dbus_message_new_method_return (message);
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (reply);
+
+ out:
+  if (volumes != NULL)
+    {
+      g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
+      g_list_free (volumes);
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 volume_mount_cb (GVolume *volume, GAsyncResult *result, DBusMessage *message)
 {
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in volume_mount_cb");
+
+  g_object_set_data (G_OBJECT (volume), "mount_operation", NULL);
+  g_object_set_data (G_OBJECT (volume), "cancellable", NULL);
+
   error = NULL;
   if (!g_volume_mount_finish (volume, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -492,26 +804,170 @@
   dbus_message_unref (reply);
 }
 
+static void
+ask_password_cb (GMountOperation  *mount_operation,
+                 const gchar      *message_to_show,
+                 const gchar      *default_user,
+                 const gchar      *default_domain,
+                 GAskPasswordFlags flags,
+                 gpointer          user_data)
+{
+  gchar *id;
+  DBusMessage *message;
+  DBusMessageIter iter;
+  GVolume *volume;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+
+  print_debug ("in ask_password_cb %s", message_to_show);
+
+  volume = G_VOLUME (user_data);
+
+  id = g_strdup_printf ("%p", volume);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     "MountOpAskPassword");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  if (message_to_show == NULL)
+    message_to_show = "";
+
+  if (default_user == NULL)
+    default_user = "";
+
+  if (default_domain == NULL)
+    default_domain = "";
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_user);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_domain);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &flags);
+
+  dbus_message_set_destination (message, mount_op_owner);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+  g_free (id);
+}
+
+static void
+ask_question_cb (GMountOperation  *mount_operation,
+                 const gchar      *message_to_show,
+                 gchar           **choices,
+                 gpointer          user_data)
+{
+  gchar *id;
+  DBusMessage *message;
+  DBusMessageIter iter;
+  DBusMessageIter iter_string_array;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+  GVolume *volume;
+  guint n;
+
+  print_debug ("in ask_question_cb %s", message_to_show);
+
+  volume = G_VOLUME (user_data);
+
+  id = g_strdup_printf ("%p", volume);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     "MountOpAskQuestion");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  if (message_to_show == NULL)
+    message_to_show = "";
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &iter_string_array);
+  for (n = 0; choices != NULL && choices[n] != NULL; n++)
+    dbus_message_iter_append_basic (&iter_string_array, DBUS_TYPE_STRING, &(choices[n]));
+  dbus_message_iter_close_container (&iter, &iter_string_array);
+
+  dbus_message_set_destination (message, mount_op_owner);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+  g_free (id);
+}
+
+static void
+aborted_cb (GMountOperation  *mount_operation,
+            gpointer          user_data)
+{
+  gchar *id;
+  DBusMessage *message;
+  DBusMessageIter iter;
+  GVolume *volume;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+
+  print_debug ("in aborted_cb");
+
+  volume = G_VOLUME (user_data);
+
+  id = g_strdup_printf ("%p", volume);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     "MountOpAborted");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  dbus_message_set_destination (message, mount_op_owner);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+  g_free (id);
+}
+
 static DBusHandlerResult
 handle_volume_mount (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
   dbus_uint32_t mount_flags;
-  dbus_bool_t use_mount_operation;
+  const char *mount_op_id;
   DBusError dbus_error;
   GList *volumes, *l;
   GVolume *volume;
   DBusHandlerResult ret;
   GMountOperation *mount_operation;
+  GCancellable *cancellable;
 
-  volume = NULL;
+  volumes = NULL;
   ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
   dbus_error_init (&dbus_error);
   if (!dbus_message_get_args (message, &dbus_error,
                               DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_STRING, &cancellation_id,
                               DBUS_TYPE_UINT32, &mount_flags,
-                              DBUS_TYPE_BOOLEAN, &use_mount_operation,
+                              DBUS_TYPE_STRING, &mount_op_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for VolumeMount(): %s: %s", dbus_error.name, dbus_error.message);
@@ -519,8 +975,12 @@
       goto out;
     }
 
+  print_debug ("in handle_volume_mount");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   volume = NULL;
   volumes = g_volume_monitor_get_volumes (monitor);
   for (l = volumes; l != NULL; l = l->next)
@@ -548,20 +1008,45 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (volume), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
   mount_operation = NULL;
-  if (use_mount_operation)
-    mount_operation = g_mount_operation_new ();
+  if (mount_op_id != NULL && strlen (mount_op_id) > 0)
+    {
+      mount_operation = g_proxy_mount_operation_new ();
+      g_signal_connect (mount_operation, "ask-password", G_CALLBACK (ask_password_cb), volume);
+      g_signal_connect (mount_operation, "ask-question", G_CALLBACK (ask_question_cb), volume);
+      g_signal_connect (mount_operation, "aborted", G_CALLBACK (aborted_cb), volume);
+      g_object_set_data_full (G_OBJECT (mount_operation), "mount_op_id", g_strdup (mount_op_id), g_free);
+      g_object_set_data_full (G_OBJECT (mount_operation), "mount_op_owner", g_strdup (sender), g_free);
+      g_object_set_data_full (G_OBJECT (volume), "mount_operation", mount_operation, g_object_unref);
+    }
 
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (volume), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
+
   g_volume_mount (volume,
                   mount_flags,
                   mount_operation,
-                  NULL,
+                  cancellable,
                   (GAsyncReadyCallback) volume_mount_cb,
                   dbus_message_ref (message));
 
-  if (mount_operation != NULL)
-    g_object_unref (mount_operation);
-
  out:
   if (volumes != NULL)
     {
@@ -579,14 +1064,20 @@
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in drive_eject_cb");
+
+  g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+
   error = NULL;
   if (!g_drive_eject_finish (drive, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -599,6 +1090,9 @@
 handle_drive_eject (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
+  GCancellable *cancellable;
   dbus_uint32_t unmount_flags;
   DBusError dbus_error;
   GList *drives, *l;
@@ -606,12 +1100,14 @@
   DBusHandlerResult ret;
 
   drive = NULL;
+  drives = NULL;
   unmount_flags = 0;
   ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
   dbus_error_init (&dbus_error);
   if (!dbus_message_get_args (message, &dbus_error,
                               DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_STRING, &cancellation_id,
                               DBUS_TYPE_UINT32 &unmount_flags,
                               DBUS_TYPE_INVALID))
     {
@@ -620,8 +1116,12 @@
       goto out;
     }
 
+  print_debug ("in handle_drive_eject");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   drive = NULL;
   drives = g_volume_monitor_get_connected_drives (monitor);
   for (l = drives; l != NULL; l = l->next)
@@ -649,9 +1149,29 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (drive), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (drive), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
+
   g_drive_eject (drive,
                  unmount_flags,
-                 NULL,
+                 cancellable,
                  (GAsyncReadyCallback) drive_eject_cb,
                  dbus_message_ref (message));
 
@@ -672,14 +1192,20 @@
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in drive_poll_for_media_cb");
+
+  g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+
   error = NULL;
   if (!g_drive_poll_for_media_finish (drive, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -692,17 +1218,22 @@
 handle_drive_poll_for_media (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
+  GCancellable *cancellable;
   DBusError dbus_error;
   GList *drives, *l;
   GDrive *drive;
   DBusHandlerResult ret;
 
   drive = NULL;
+  drives = NULL;
   ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
   dbus_error_init (&dbus_error);
   if (!dbus_message_get_args (message, &dbus_error,
                               DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_STRING, &cancellation_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for DrivePollForMedia(): %s: %s", dbus_error.name, dbus_error.message);
@@ -710,8 +1241,12 @@
       goto out;
     }
 
+  print_debug ("in handle_drive_poll_for_media");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   drive = NULL;
   drives = g_volume_monitor_get_connected_drives (monitor);
   for (l = drives; l != NULL; l = l->next)
@@ -739,8 +1274,28 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (drive), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (drive), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
+
   g_drive_poll_for_media (drive,
-                          NULL,
+                          cancellable,
                           (GAsyncReadyCallback) drive_poll_for_media_cb,
                           dbus_message_ref (message));
 
@@ -762,6 +1317,8 @@
   DBusMessage *reply;
   DBusMessageIter iter;
 
+  print_debug ("in handle_supported");
+
   /* if monitor wasn't created on startup; try again */
   if (monitor == NULL)
     monitor_try_create ();
@@ -780,41 +1337,158 @@
 /* ---------------------------------------------------------------------------------------------------- */
 
 static DBusHandlerResult
+handle_cancel_operation (DBusConnection *connection, DBusMessage *message)
+{
+  DBusMessage *reply;
+  DBusMessageIter iter;
+  DBusError dbus_error;
+  dbus_bool_t was_cancelled;
+  const char *sender;
+  const char *cancellation_id;
+  GList *l;
+
+  was_cancelled = FALSE;
+
+  sender = dbus_message_get_sender (message);
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message, &dbus_error,
+                              DBUS_TYPE_STRING, &cancellation_id,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for CancelOperation(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  print_debug ("in handle_cancel_operation");
+
+  /* Find GCancellable to cancel */
+  for (l = outstanding_ops; l != NULL; l = l->next)
+    {
+      GCancellable *cancellable = G_CANCELLABLE (l->data);
+      const gchar *owner;
+      const gchar *id;
+
+      owner = g_object_get_data (G_OBJECT (cancellable), "owner");
+      id = g_object_get_data (G_OBJECT (cancellable), "cancellation_id");
+      if (g_strcmp0 (owner, sender) == 0 && g_strcmp0 (id, cancellation_id) == 0)
+        {
+          print_debug ("found op to cancel");
+          g_cancellable_cancel (cancellable);
+
+          was_cancelled = TRUE;
+          break;
+        }
+    }
+
+  if (!was_cancelled)
+    g_warning ("didn't find op to cancel");
+
+ out:
+  reply = dbus_message_new_method_return (message);
+  dbus_message_iter_init_append (reply, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &was_cancelled);
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (reply);
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static DBusHandlerResult
 filter_function (DBusConnection *connection, DBusMessage *message, void *user_data)
 {
   DBusHandlerResult ret;
 
   ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-  if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "IsSupported") &&
-      strcmp (dbus_message_get_path (message), "/") == 0)
+  if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameLost"))
     {
-      ret = handle_is_supported (connection, message);
+      /* means that someone has claimed our name (we allow replacement) */
+      g_warning ("Got NameLost, some other instance replaced us");
+      exit (0);
     }
-  else
+  else if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameOwnerChanged"))
     {
-      if (monitor != NULL)
+      DBusMessageIter iter;
+      const gchar *name;
+      const gchar *old_owner;
+      const gchar *new_owner;
+
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &old_owner);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &new_owner);
+      dbus_message_iter_next (&iter);
+
+      print_debug ("NameOwnerChanged: '%s' '%s' '%s'", name, old_owner, new_owner);
+
+      if (strlen (new_owner) == 0)
         {
-          if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "List") &&
-              strcmp (dbus_message_get_path (message), "/") == 0)
-            ret = handle_list (connection, message);
+          GList *l;
 
-          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountUnmount") &&
-                   strcmp (dbus_message_get_path (message), "/") == 0)
-            ret = handle_mount_unmount (connection, message);
+          /* see if @name has outstanding ops; if so, cancel them */
+          for (l = outstanding_ops; l != NULL; l = l->next)
+            {
+              GCancellable *cancellable = G_CANCELLABLE (l->data);
+              const gchar *owner;
 
-          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeMount") &&
-                   strcmp (dbus_message_get_path (message), "/") == 0)
-            ret = handle_volume_mount (connection, message);
+              owner = g_object_get_data (G_OBJECT (cancellable), "owner");
+              print_debug ("looking at op for %s", owner);
+              if (g_strcmp0 (owner, name) == 0)
+                {
+                  print_debug ("****** name has an outstanding op");
+                  g_cancellable_cancel (cancellable);
+                }
+            }
 
-          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEject") &&
-                   strcmp (dbus_message_get_path (message), "/") == 0)
-            ret = handle_drive_eject (connection, message);
+          remove_name_owned_changed_for_unique_name (name);
+        }
 
-          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DrivePollForMedia") &&
-                   strcmp (dbus_message_get_path (message), "/") == 0)
-            ret = handle_drive_poll_for_media (connection, message);
+    }
+  else if (g_strcmp0 (dbus_message_get_interface (message), "org.gtk.Private.RemoteVolumeMonitor") == 0 &&
+           g_strcmp0 (dbus_message_get_path (message), "/org/gtk/Private/RemoteVolumeMonitor") == 0)
+    {
+      /* If someone is calling into this object and interface, start watching their name so
+       * we can cancel operations initiated by them when they disconnect
+       */
+      ensure_name_owner_changed_for_unique_name (dbus_message_get_sender (message));
+
+      if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "IsSupported"))
+        {
+          ret = handle_is_supported (connection, message);
         }
+      else
+        {
+          if (monitor != NULL)
+            {
+              if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "List"))
+                ret = handle_list (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "CancelOperation"))
+                ret = handle_cancel_operation (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountUnmount"))
+                ret = handle_mount_unmount (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpReply"))
+                ret = handle_mount_op_reply (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeMount"))
+                ret = handle_volume_mount (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEject"))
+                ret = handle_drive_eject (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DrivePollForMedia"))
+                ret = handle_drive_poll_for_media (connection, message);
+
+            }
+        }
     }
 
   return ret;
@@ -831,7 +1505,9 @@
 
   id = g_strdup_printf ("%p", object);
 
-  message = dbus_message_new_signal ("/", "org.gtk.Private.RemoteVolumeMonitor", signal_name);
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     signal_name);
   dbus_message_iter_init_append (message, &iter);
   dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
   dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
@@ -1006,6 +1682,7 @@
 
   the_volume_monitor_type = volume_monitor_type;
   the_dbus_name = dbus_name;
+  unique_names_being_watched = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
   /* try and create the monitor */
   monitor_try_create ();
@@ -1021,7 +1698,12 @@
 
   _g_dbus_connection_integrate_with_main (connection);
 
-  rc = dbus_bus_request_name (connection, dbus_name, 0, &dbus_error);
+  rc = dbus_bus_request_name (connection,
+                              dbus_name,
+                              DBUS_NAME_FLAG_ALLOW_REPLACEMENT |
+                              DBUS_NAME_FLAG_DO_NOT_QUEUE |
+                              DBUS_NAME_FLAG_REPLACE_EXISTING,
+                              &dbus_error);
   if (dbus_error_is_set (&dbus_error))
     {
       g_warning ("dbus_bus_request_name failed: %s: %s", dbus_error.name, dbus_error.message);
Index: monitor/proxy/gproxyvolumemonitor.h
===================================================================
--- monitor/proxy/gproxyvolumemonitor.h	(revision 2238)
+++ monitor/proxy/gproxyvolumemonitor.h	(working copy)
@@ -31,11 +31,12 @@
 
 G_BEGIN_DECLS
 
-#define G_TYPE_PROXY_VOLUME_MONITOR        (g_proxy_volume_monitor_get_type ())
-#define G_PROXY_VOLUME_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_PROXY_VOLUME_MONITOR, GProxyVolumeMonitor))
-#define G_PROXY_VOLUME_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_PROXY_VOLUME_MONITOR, GProxyVolumeMonitorClass))
-#define G_IS_PROXY_VOLUME_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_PROXY_VOLUME_MONITOR))
-#define G_IS_PROXY_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_PROXY_VOLUME_MONITOR))
+#define G_TYPE_PROXY_VOLUME_MONITOR         (g_proxy_volume_monitor_get_type ())
+#define G_PROXY_VOLUME_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_PROXY_VOLUME_MONITOR, GProxyVolumeMonitor))
+#define G_PROXY_VOLUME_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_PROXY_VOLUME_MONITOR, GProxyVolumeMonitorClass))
+#define G_PROXY_VOLUME_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_PROXY_VOLUME_MONITOR, GProxyVolumeMonitorClass))
+#define G_IS_PROXY_VOLUME_MONITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_PROXY_VOLUME_MONITOR))
+#define G_IS_PROXY_VOLUME_MONITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_PROXY_VOLUME_MONITOR))
 
 typedef struct _GProxyVolumeMonitor GProxyVolumeMonitor;
 typedef struct _GProxyVolumeMonitorClass GProxyVolumeMonitorClass;



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