summaryrefslogtreecommitdiff
path: root/monitor
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2009-06-17 09:49:44 -0400
committerDavid Zeuthen <davidz@redhat.com>2009-06-17 09:50:35 -0400
commit4053577a19585c63e23e6c6e85011e2df9910745 (patch)
tree8c77e6ab423827a8623e7b55ad77ef362eb669af /monitor
parent8a72222d454bdf12e88402b2f012667e7c8e9754 (diff)
downloadgvfs-4053577a19585c63e23e6c6e85011e2df9910745.tar.gz
Bug 585591 – Starting/stopping drives
This is the GVfs implementation for the new GIO API for starting/stopping drives. See http://bugzilla.gnome.org/show_bug.cgi?id=585591 for details.
Diffstat (limited to 'monitor')
-rw-r--r--monitor/gdu/ggdudrive.c676
-rw-r--r--monitor/gdu/ggduvolumemonitor.c57
-rw-r--r--monitor/proxy/gproxydrive.c626
-rw-r--r--monitor/proxy/gproxydrive.h9
-rw-r--r--monitor/proxy/gproxyvolume.c2
-rw-r--r--monitor/proxy/gproxyvolumemonitor.c33
-rw-r--r--monitor/proxy/gproxyvolumemonitor.h8
-rw-r--r--monitor/proxy/gvfsproxyvolumemonitordaemon.c555
8 files changed, 1816 insertions, 150 deletions
diff --git a/monitor/gdu/ggdudrive.c b/monitor/gdu/ggdudrive.c
index d332e14b..c89928ca 100644
--- a/monitor/gdu/ggdudrive.c
+++ b/monitor/gdu/ggdudrive.c
@@ -51,6 +51,10 @@ struct _GGduDrive {
gboolean can_eject;
gboolean can_poll_for_media;
gboolean is_media_check_automatic;
+
+ GDriveStartStopType start_stop_type;
+ gboolean can_start;
+ gboolean can_stop;
};
static void g_gdu_drive_drive_iface_init (GDriveIface *iface);
@@ -126,6 +130,9 @@ update_drive (GGduDrive *drive)
gboolean old_is_media_removable;
gboolean old_has_media;
gboolean old_can_eject;
+ gboolean old_can_start;
+ gboolean old_can_stop;
+ gboolean old_start_stop_type;
gboolean old_is_media_check_automatic;
gboolean old_can_poll_for_media;
@@ -133,6 +140,9 @@ update_drive (GGduDrive *drive)
old_is_media_removable = drive->is_media_removable;
old_has_media = drive->has_media;
old_can_eject = drive->can_eject;
+ old_can_start = drive->can_start;
+ old_can_stop = drive->can_stop;
+ old_start_stop_type = drive->start_stop_type;
old_can_poll_for_media = drive->can_poll_for_media;
old_is_media_check_automatic = drive->is_media_check_automatic;
@@ -161,6 +171,7 @@ update_drive (GGduDrive *drive)
drive->is_media_removable = TRUE;
drive->has_media = TRUE;
drive->can_eject = FALSE;
+ drive->can_poll_for_media = FALSE;
}
else
{
@@ -171,12 +182,60 @@ update_drive (GGduDrive *drive)
/* All drives with removable media are ejectable
*
* See http://bugzilla.gnome.org/show_bug.cgi?id=576587 for why we want this.
+ *
+ * See also below where we e.g. set can_eject to TRUE for non-removable drives.
*/
- drive->can_eject = gdu_device_drive_get_is_media_ejectable (device) || gdu_device_drive_get_requires_eject (device) || gdu_device_is_removable (device) || gdu_device_drive_get_can_detach (device);
+ drive->can_eject = gdu_device_drive_get_is_media_ejectable (device) || gdu_device_drive_get_requires_eject (device) || gdu_device_is_removable (device);
drive->is_media_check_automatic = gdu_device_is_media_change_detected (device);
drive->can_poll_for_media = TRUE;
}
+ /* determine start/stop type */
+ drive->can_stop = FALSE;
+ drive->can_start = FALSE;
+ drive->start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
+ if (gdu_drive_is_activatable (GDU_DRIVE (drive->presentable)))
+ {
+ drive->can_stop = gdu_drive_can_deactivate (GDU_DRIVE (drive->presentable));
+ drive->can_start = gdu_drive_can_activate (GDU_DRIVE (drive->presentable), NULL);
+ drive->start_stop_type = G_DRIVE_START_STOP_TYPE_MULTIDISK;
+ }
+ else if (device != NULL && gdu_device_drive_get_can_detach (device))
+ {
+ /* If the device is not ejectable, just detach on Eject() and claim to be ejectable.
+ *
+ * This is so we get the UI to display "Eject" instead of "Shutdown" since it is
+ * more familiar and the common case. The way this works is that after the Eject()
+ * method returns we call Detach() - see eject_cb() below.
+ *
+ * (Note that it's not enough to just call Detach() since some devices, such as
+ * the Kindle, only works with Eject(). So we call them both in order)
+ */
+ if (!gdu_device_drive_get_is_media_ejectable (device))
+ {
+ drive->can_eject = TRUE;
+ /* we still set this since
+ *
+ * a) it helps when debugging things using gvfs-mount(1) output
+ * since the tool will print can_stop=0 but start_stop_type=shutdown
+ *
+ * b) we use it in eject_cb() to determine we need to call Detach()
+ * after Eject() successfully completes
+ */
+ drive->start_stop_type = G_DRIVE_START_STOP_TYPE_SHUTDOWN;
+ }
+ else
+ {
+ /* So here the device is ejectable and detachable - for example, a USB CD-ROM
+ * drive or a CD-ROM drive in an Ultrabay - for these, we want to offer both
+ * "Eject" and "Shutdown" options in the UI
+ */
+ drive->can_stop = TRUE;
+ drive->can_start = FALSE;
+ drive->start_stop_type = G_DRIVE_START_STOP_TYPE_SHUTDOWN;
+ }
+ }
+
if (device != NULL)
g_object_unref (device);
@@ -193,6 +252,9 @@ update_drive (GGduDrive *drive)
changed = !((old_is_media_removable == drive->is_media_removable) &&
(old_has_media == drive->has_media) &&
(old_can_eject == drive->can_eject) &&
+ (old_can_start == drive->can_start) &&
+ (old_can_stop == drive->can_stop) &&
+ (old_start_stop_type == drive->start_stop_type) &&
(old_is_media_check_automatic == drive->is_media_check_automatic) &&
(old_can_poll_for_media == drive->can_poll_for_media) &&
(g_strcmp0 (old_name, drive->name) == 0) &&
@@ -363,94 +425,34 @@ g_gdu_drive_can_poll_for_media (GDrive *_drive)
return drive->can_poll_for_media;
}
-static void
-detach_cb (GduDevice *device,
- GError *error,
- gpointer user_data)
+static gboolean
+g_gdu_drive_can_start (GDrive *_drive)
{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
- if (error != NULL)
- {
- g_simple_async_result_set_from_error (simple, error);
- g_error_free (error);
- }
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
+ return drive->can_start;
}
-static void
-eject_cb (GduDevice *device,
- GError *error,
- gpointer user_data)
+static gboolean
+g_gdu_drive_can_stop (GDrive *_drive)
{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
- if (error != NULL)
- {
- g_simple_async_result_set_from_error (simple, error);
- g_error_free (error);
- }
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
+ return drive->can_stop;
}
-
-static void
-g_gdu_drive_eject_do (GDrive *_drive,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+static GDriveStartStopType
+g_gdu_drive_get_start_stop_type (GDrive *_drive)
{
GGduDrive *drive = G_GDU_DRIVE (_drive);
- GSimpleAsyncResult *simple;
- GduDevice *device;
+ return drive->start_stop_type;
+}
- device = gdu_presentable_get_device (drive->presentable);
- if (device == NULL)
- {
- simple = g_simple_async_result_new_error (G_OBJECT (drive),
- callback,
- user_data,
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- "Drive is activatable and not running");
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
- }
- else
- {
- simple = g_simple_async_result_new (G_OBJECT (drive),
- callback,
- user_data,
- NULL);
+/* ---------------------------------------------------------------------------------------------------- */
- /* Note that we also offer the Eject option for non-removable
- * devices (see update_drive() above) that are detachable so the
- * device may actually not be removable when we get here...
- *
- * However, keep in mind that a device may be both removable and
- * detachable (e.g. a usb optical drive)..
- *
- * Now, consider what would happen if we detached a USB optical
- * drive? The device would power down without actually ejecting
- * the media... and it would require a power-cycle or a replug
- * to use it for other media. Therefore, never detach devices
- * with removable media, only eject them.
- */
- if (gdu_device_drive_get_can_detach (device) && !gdu_device_is_removable (device))
- {
- gdu_device_op_drive_detach (device, detach_cb, simple);
- }
- else
- {
- gdu_device_op_drive_eject (device, eject_cb, simple);
- }
- g_object_unref (device);
- }
-}
+typedef void (*UnmountsMountsFunc) (GDrive *drive,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer on_all_unmounted_data);
typedef struct {
GDrive *drive;
@@ -460,6 +462,9 @@ typedef struct {
GMountUnmountFlags flags;
GList *pending_mounts;
+
+ UnmountsMountsFunc on_all_unmounted;
+ gpointer on_all_unmounted_data;
} UnmountMountsOp;
static void
@@ -475,12 +480,48 @@ free_unmount_mounts_op (UnmountMountsOp *data)
g_list_free (data->pending_mounts);
}
-static void _eject_unmount_mounts (UnmountMountsOp *data);
+static void
+unmount_mounts_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data);
static void
-_eject_unmount_mounts_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+unmount_mounts_do (UnmountMountsOp *data)
+{
+ if (data->pending_mounts == NULL)
+ {
+
+ /*g_warning ("all pending mounts done");*/
+ data->on_all_unmounted (data->drive,
+ data->cancellable,
+ data->callback,
+ data->user_data,
+ data->on_all_unmounted_data);
+
+ g_object_unref (data->drive);
+ g_free (data);
+ }
+ else
+ {
+ GMount *mount;
+
+ mount = data->pending_mounts->data;
+ data->pending_mounts = g_list_remove (data->pending_mounts, mount);
+
+ /*g_warning ("unmounting %p", mount);*/
+
+ g_mount_unmount (mount,
+ data->flags,
+ data->cancellable,
+ unmount_mounts_cb,
+ data);
+ }
+}
+
+static void
+unmount_mounts_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
UnmountMountsOp *data = user_data;
GMount *mount = G_MOUNT (source_object);
@@ -514,86 +555,471 @@ _eject_unmount_mounts_cb (GObject *source_object,
/*g_warning ("successfully unmounted %p", mount);*/
/* move on to the next mount.. */
- _eject_unmount_mounts (data);
+ unmount_mounts_do (data);
}
g_object_unref (mount);
}
static void
-_eject_unmount_mounts (UnmountMountsOp *data)
+unmount_mounts (GGduDrive *drive,
+ GMountUnmountFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ UnmountsMountsFunc on_all_unmounted,
+ gpointer on_all_unmounted_data)
{
GMount *mount;
+ UnmountMountsOp *data;
+ GList *l;
- if (data->pending_mounts == NULL)
+ data = g_new0 (UnmountMountsOp, 1);
+ data->drive = g_object_ref (drive);
+ data->cancellable = cancellable;
+ data->callback = callback;
+ data->user_data = user_data;
+ data->flags = flags;
+ data->on_all_unmounted = on_all_unmounted;
+ data->on_all_unmounted_data = on_all_unmounted_data;
+
+ for (l = drive->volumes; l != NULL; l = l->next)
{
+ GGduVolume *volume = l->data;
+ mount = g_volume_get_mount (G_VOLUME (volume));
+ if (mount != NULL && g_mount_can_unmount (mount))
+ data->pending_mounts = g_list_prepend (data->pending_mounts, g_object_ref (mount));
+ }
- /*g_warning ("all pending mounts done; ejecting drive");*/
+ unmount_mounts_do (data);
+}
- g_gdu_drive_eject_do (data->drive,
- data->cancellable,
- data->callback,
- data->user_data);
+/* ---------------------------------------------------------------------------------------------------- */
- g_object_unref (data->drive);
- g_free (data);
+static void
+detach_after_eject_cb (GduDevice *device,
+ GError *error,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+ /* Don't return an error here - this is because some devices, such as
+ * the Kindle, can do Eject() but not Detach() e.g. the STOP UNIT
+ * command or any other part of Detach() may fail.
+ */
+ if (error != NULL)
+ {
+ g_warning ("Detach() after Eject() failed with: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+eject_cb (GduDevice *device,
+ GError *error,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+ GGduDrive *drive;
+
+ if (error != NULL)
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ g_error_free (error);
+ goto out;
+ }
+
+ drive = G_GDU_DRIVE (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+ if (drive->can_stop == FALSE && drive->start_stop_type == G_DRIVE_START_STOP_TYPE_SHUTDOWN)
+ {
+ /* If device is not ejectable but it is detachable and we don't support stop(),
+ * then also run Detach() after Eject() - see update_drive() for details for why...
+ */
+ gdu_device_op_drive_detach (device, detach_after_eject_cb, simple);
}
else
{
- mount = data->pending_mounts->data;
- data->pending_mounts = g_list_remove (data->pending_mounts, mount);
+ /* otherwise we are done */
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ }
+ g_object_unref (drive);
- /*g_warning ("unmounting %p", mount);*/
+ out:
+ ;
+}
- g_mount_unmount (mount,
- data->flags,
- data->cancellable,
- _eject_unmount_mounts_cb,
- data);
+static void
+g_gdu_drive_eject_on_all_unmounted (GDrive *_drive,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer on_all_unmounted_data)
+{
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
+ GSimpleAsyncResult *simple;
+ GduDevice *device;
+
+ device = gdu_presentable_get_device (drive->presentable);
+ if (device == NULL)
+ {
+ simple = g_simple_async_result_new_error (G_OBJECT (drive),
+ callback,
+ user_data,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Drive is activatable and not running");
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ else
+ {
+ simple = g_simple_async_result_new (G_OBJECT (drive),
+ callback,
+ user_data,
+ NULL);
+
+ gdu_device_op_drive_eject (device, eject_cb, simple);
}
}
static void
-g_gdu_drive_eject (GDrive *drive,
+g_gdu_drive_eject (GDrive *_drive,
GMountUnmountFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GGduDrive *gdu_drive = G_GDU_DRIVE (drive);
- UnmountMountsOp *data;
- GList *l;
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
/* first we need to go through all the volumes and unmount their assoicated mounts (if any) */
+ unmount_mounts (drive,
+ flags,
+ cancellable,
+ callback,
+ user_data,
+ g_gdu_drive_eject_on_all_unmounted,
+ NULL);
+}
- data = g_new0 (UnmountMountsOp, 1);
- data->drive = g_object_ref (drive);
- data->cancellable = cancellable;
- data->callback = callback;
- data->user_data = user_data;
- data->flags = flags;
+static gboolean
+g_gdu_drive_eject_finish (GDrive *drive,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
- for (l = gdu_drive->volumes; l != NULL; l = l->next)
+static void
+stop_cb (GduDevice *device,
+ GError *error,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+ if (error != NULL)
{
- GGduVolume *volume = l->data;
- GMount *mount;
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
- mount = g_volume_get_mount (G_VOLUME (volume));
- if (mount != NULL && g_mount_can_unmount (mount))
- data->pending_mounts = g_list_prepend (data->pending_mounts, g_object_ref (mount));
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+drive_deactivate_cb (GduDrive *drive,
+ GError *error,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+ if (error != NULL)
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
}
- _eject_unmount_mounts (data);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+g_gdu_drive_stop_on_all_unmounted (GDrive *_drive,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer on_all_unmounted_data)
+{
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
+ GSimpleAsyncResult *simple;
+ GduDevice *device;
+
+ device = gdu_presentable_get_device (drive->presentable);
+ if (device == NULL)
+ {
+ simple = g_simple_async_result_new_error (G_OBJECT (drive),
+ callback,
+ user_data,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Drive is activatable and not running");
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ else
+ {
+ simple = g_simple_async_result_new (G_OBJECT (drive),
+ callback,
+ user_data,
+ NULL);
+
+ switch (drive->start_stop_type)
+ {
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ gdu_device_op_drive_detach (device, stop_cb, simple);
+ break;
+
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ gdu_drive_deactivate (GDU_DRIVE (drive->presentable), drive_deactivate_cb, simple);
+ break;
+
+ default:
+ g_simple_async_result_set_error (simple,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "start_stop_type %d not supported",
+ drive->start_stop_type);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ break;
+ }
+ }
+}
+
+static void
+g_gdu_drive_stop (GDrive *_drive,
+ GMountUnmountFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
+
+ /* first we need to go through all the volumes and unmount their assoicated mounts (if any) */
+ unmount_mounts (drive,
+ flags,
+ cancellable,
+ callback,
+ user_data,
+ g_gdu_drive_stop_on_all_unmounted,
+ NULL);
}
static gboolean
-g_gdu_drive_eject_finish (GDrive *drive,
+g_gdu_drive_stop_finish (GDrive *drive,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+start_cb (GduDrive *drive,
+ gchar *assembled_drive_object_path,
+ GError *error,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+ if (error != NULL)
+ {
+ g_simple_async_result_set_error (simple,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Failed activating drive: %s",
+ error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_free (assembled_drive_object_path);
+ }
+ g_simple_async_result_complete (simple);
+}
+
+typedef struct
+{
+ GGduDrive *drive;
+ GSimpleAsyncResult *simple;
+
+ GMountOperation *start_operation;
+ gulong start_operation_reply_handler_id;
+} StartOpData;
+
+static void
+start_operation_reply (GMountOperation *op,
+ GMountOperationResult result,
+ gpointer user_data)
+{
+ StartOpData *data = user_data;
+ gint choice;
+
+ /* we got what we wanted; don't listen to any other signals from the start operation */
+ if (data->start_operation_reply_handler_id != 0)
+ {
+ g_signal_handler_disconnect (data->start_operation, data->start_operation_reply_handler_id);
+ data->start_operation_reply_handler_id = 0;
+ }
+
+ if (result != G_MOUNT_OPERATION_HANDLED)
+ {
+ if (result == G_MOUNT_OPERATION_ABORTED)
+ {
+ /* The user aborted the operation so consider it "handled" */
+ g_simple_async_result_set_error (data->simple,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED_HANDLED,
+ "Start operation dialog aborted (user should never see this error since "
+ "it is G_IO_ERROR_FAILED_HANDLED)");
+ }
+ else
+ {
+ g_simple_async_result_set_error (data->simple,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Expected G_MOUNT_OPERATION_HANDLED but got %d", result);
+ }
+ g_simple_async_result_complete (data->simple);
+ goto out;
+ }
+
+ /* handle the user pressing cancel */
+ choice = g_mount_operation_get_choice (data->start_operation);
+ if (choice == 1)
+ {
+ g_simple_async_result_set_error (data->simple,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED_HANDLED,
+ "User refused to start degraded array (user should never see this error since "
+ "it is G_IO_ERROR_FAILED_HANDLED)");
+ g_simple_async_result_complete (data->simple);
+ goto out;
+ }
+
+ gdu_drive_activate (GDU_DRIVE (data->drive->presentable), start_cb, data->simple);
+
+ out:
+ g_object_unref (data->drive);
+ g_object_unref (data->start_operation);
+ g_free (data);
+}
+
+static void
+g_gdu_drive_start (GDrive *_drive,
+ GDriveStartFlags flags,
+ GMountOperation *start_operation,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GGduDrive *drive = G_GDU_DRIVE (_drive);
+ GSimpleAsyncResult *simple;
+ gboolean degraded;
+
+ /* TODO: handle GCancellable */
+
+ if (!gdu_drive_can_activate (GDU_DRIVE (drive->presentable), &degraded))
+ goto not_supported;
+
+ if (start_operation == NULL && degraded)
+ goto refuse_degraded_without_confirmation;
+
+ simple = g_simple_async_result_new (G_OBJECT (drive),
+ callback,
+ user_data,
+ NULL);
+
+ if (degraded)
+ {
+ const gchar *message;
+ const gchar *choices[3];
+ StartOpData *data;
+
+ message = _("Start drive in degraded mode?\n"
+ "Starting a drive in degraded mode means that "
+ "the drive is no longer tolerant to failures. "
+ "Data on the drive may be irrevocably lost if a "
+ "component fails.");
+
+ choices[0] = _("Start Anyway");
+ choices[1] = _("Cancel");
+ choices[2] = NULL;
+
+ data = g_new0 (StartOpData, 1);
+ data->drive = g_object_ref (drive);
+ data->simple = simple;
+ data->start_operation = g_object_ref (start_operation);
+ data->start_operation_reply_handler_id = g_signal_connect (start_operation,
+ "reply",
+ G_CALLBACK (start_operation_reply),
+ data);
+
+ g_signal_emit_by_name (start_operation,
+ "ask-question",
+ message,
+ choices);
+ }
+ else
+ {
+ gdu_drive_activate (GDU_DRIVE (drive->presentable), start_cb, simple);
+ }
+
+ return;
+
+ not_supported:
+ simple = g_simple_async_result_new_error (G_OBJECT (drive),
+ callback,
+ user_data,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Starting drive with start_stop_type %d is not supported",
+ drive->start_stop_type);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+
+ refuse_degraded_without_confirmation:
+ simple = g_simple_async_result_new_error (G_OBJECT (drive),
+ callback,
+ user_data,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Refusing to start degraded multidisk drive without user confirmation");
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+
+}
+
+static gboolean
+g_gdu_drive_start_finish (GDrive *drive,
GAsyncResult *result,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
}
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
poll_media_cb (GduDevice *device,
GError *error,
@@ -656,6 +1082,8 @@ g_gdu_drive_poll_for_media_finish (GDrive *drive,
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
}
+/* ---------------------------------------------------------------------------------------------------- */
+
static char *
g_gdu_drive_get_identifier (GDrive *_drive,
const char *kind)
@@ -688,6 +1116,8 @@ g_gdu_drive_enumerate_identifiers (GDrive *_drive)
return (gchar **) g_ptr_array_free (p, FALSE);
}
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
g_gdu_drive_drive_iface_init (GDriveIface *iface)
{
@@ -706,6 +1136,14 @@ g_gdu_drive_drive_iface_init (GDriveIface *iface)
iface->poll_for_media_finish = g_gdu_drive_poll_for_media_finish;
iface->get_identifier = g_gdu_drive_get_identifier;
iface->enumerate_identifiers = g_gdu_drive_enumerate_identifiers;
+
+ iface->get_start_stop_type = g_gdu_drive_get_start_stop_type;
+ iface->can_start = g_gdu_drive_can_start;
+ iface->can_stop = g_gdu_drive_can_stop;
+ iface->start = g_gdu_drive_start;
+ iface->start_finish = g_gdu_drive_start_finish;
+ iface->stop = g_gdu_drive_stop;
+ iface->stop_finish = g_gdu_drive_stop_finish;
}
gboolean
diff --git a/monitor/gdu/ggduvolumemonitor.c b/monitor/gdu/ggduvolumemonitor.c
index 6da7393d..ac90ba4b 100644
--- a/monitor/gdu/ggduvolumemonitor.c
+++ b/monitor/gdu/ggduvolumemonitor.c
@@ -836,7 +836,7 @@ should_drive_be_ignored (GduPool *pool, GduDrive *d, GList *fstab_mount_points)
{
GduDevice *device;
gboolean ignored;
- gboolean has_volumes;
+ gboolean have_volumes;
gboolean all_volumes_are_ignored;
GList *enclosed;
GList *l;
@@ -847,24 +847,26 @@ should_drive_be_ignored (GduPool *pool, GduDrive *d, GList *fstab_mount_points)
device = gdu_presentable_get_device (GDU_PRESENTABLE (d));
- /* If there is no GduDevice for a drive, then ignore it.
- *
- * Note that right now the only drives without a GduDevice are Linux
- * MD arrays not yet activated. In the future we might want to
- * display these so the user can start the array.
+ /* If there is no GduDevice for a drive, then ignore it unless
+ * we know how to start it. Right now this is only relevant
+ * for GduLinuxMdDrive but we can add other stuff to libgdu
+ * in the future and things will work here
*/
if (device == NULL)
{
- ignored = TRUE;
- goto out;
+ if (!gdu_drive_is_activatable (d))
+ {
+ ignored = TRUE;
+ goto out;
+ }
}
- if (gdu_device_get_presentation_hide (device)) {
+ if (device != NULL && gdu_device_get_presentation_hide (device)) {
ignored = TRUE;
goto out;
}
- has_volumes = FALSE;
+ have_volumes = FALSE;
all_volumes_are_ignored = TRUE;
/* never ignore a drive if it has volumes that we don't want to ignore */
@@ -878,7 +880,7 @@ should_drive_be_ignored (GduPool *pool, GduDrive *d, GList *fstab_mount_points)
{
GduVolume *volume = GDU_VOLUME (enclosed_presentable);
- has_volumes = TRUE;
+ have_volumes = TRUE;
if (!should_volume_be_ignored (pool, volume, fstab_mount_points))
{
@@ -894,23 +896,26 @@ should_drive_be_ignored (GduPool *pool, GduDrive *d, GList *fstab_mount_points)
*
* b) the volumes of the drive are all ignored
*/
- if (!has_volumes)
- {
- if (gdu_device_is_media_available (device))
- ignored = TRUE;
- }
- else
+ if (device != NULL)
{
- if (all_volumes_are_ignored)
- ignored = TRUE;
- }
+ if (!have_volumes)
+ {
+ if (gdu_device_is_media_available (device))
+ ignored = TRUE;
+ }
+ else
+ {
+ if (all_volumes_are_ignored)
+ ignored = TRUE;
+ }
- /* special case for audio discs: don't ignore the drive since we'll create
- * a cdda:// mount for the drive
- */
- if (gdu_device_is_optical_disc (device) && gdu_device_optical_disc_get_num_audio_tracks (device) > 0)
- {
- ignored = FALSE;
+ /* special case for audio discs: don't ignore the drive since we'll create
+ * a cdda:// mount for the drive
+ */
+ if (gdu_device_is_optical_disc (device) && gdu_device_optical_disc_get_num_audio_tracks (device) > 0)
+ {
+ ignored = FALSE;
+ }
}
out:
diff --git a/monitor/proxy/gproxydrive.c b/monitor/proxy/gproxydrive.c
index 79459935..5f027b84 100644
--- a/monitor/proxy/gproxydrive.c
+++ b/monitor/proxy/gproxydrive.c
@@ -53,8 +53,13 @@ struct _GProxyDrive {
gboolean is_media_check_automatic;
gboolean has_media;
gboolean is_media_removable;
+ gboolean can_start;
+ gboolean can_stop;
+ GDriveStartStopType start_stop_type;
GHashTable *identifiers;
+
+ GHashTable *hash_start_op_id_to_data;
};
static void g_proxy_drive_drive_iface_init (GDriveIface *iface);
@@ -87,6 +92,8 @@ g_proxy_drive_finalize (GObject *object)
if (drive->identifiers != NULL)
g_hash_table_unref (drive->identifiers);
+ g_hash_table_unref (drive->hash_start_op_id_to_data);
+
if (G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize)
(*G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize) (object);
}
@@ -107,6 +114,7 @@ g_proxy_drive_class_finalize (GProxyDriveClass *klass)
static void
g_proxy_drive_init (GProxyDrive *proxy_drive)
{
+ proxy_drive->hash_start_op_id_to_data = g_hash_table_new (g_str_hash, g_str_equal);
}
GProxyDrive *
@@ -129,10 +137,13 @@ g_proxy_drive_new (GProxyVolumeMonitor *volume_monitor)
* boolean has-media
* boolean is-media-removable
* boolean is-media-check-automatic
+ * boolean can-start
+ * boolean can-stop
+ * uint32 start-stop-type
* array:string volume-ids
* dict:string->string identifiers
*/
-#define DRIVE_STRUCT_TYPE "(sssbbbbasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbbbuasa{ss})"
void
g_proxy_drive_update (GProxyDrive *drive,
@@ -148,6 +159,9 @@ g_proxy_drive_update (GProxyDrive *drive,
dbus_bool_t has_media;
dbus_bool_t is_media_removable;
dbus_bool_t is_media_check_automatic;
+ dbus_bool_t can_start;
+ dbus_bool_t can_stop;
+ dbus_uint32_t start_stop_type;
GPtrArray *volume_ids;
GHashTable *identifiers;
@@ -168,6 +182,12 @@ g_proxy_drive_update (GProxyDrive *drive,
dbus_message_iter_next (&iter_struct);
dbus_message_iter_get_basic (&iter_struct, &is_media_check_automatic);
dbus_message_iter_next (&iter_struct);
+ dbus_message_iter_get_basic (&iter_struct, &can_start);
+ dbus_message_iter_next (&iter_struct);
+ dbus_message_iter_get_basic (&iter_struct, &can_stop);
+ dbus_message_iter_next (&iter_struct);
+ dbus_message_iter_get_basic (&iter_struct, &start_stop_type);
+ dbus_message_iter_next (&iter_struct);
volume_ids = g_ptr_array_new ();
dbus_message_iter_recurse (&iter_struct, &iter_volume_ids_iter);
@@ -215,6 +235,9 @@ g_proxy_drive_update (GProxyDrive *drive,
drive->has_media = has_media;
drive->is_media_removable = is_media_removable;
drive->is_media_check_automatic = is_media_check_automatic;
+ drive->can_start = can_start;
+ drive->can_stop = can_stop;
+ drive->start_stop_type = start_stop_type;
drive->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL;
drive->volume_ids = g_strdupv ((char **) volume_ids->pdata);
@@ -353,6 +376,45 @@ g_proxy_drive_can_poll_for_media (GDrive *drive)
return res;
}
+static gboolean
+g_proxy_drive_can_start (GDrive *drive)
+{
+ GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+ gboolean res;
+
+ G_LOCK (proxy_drive);
+ res = proxy_drive->can_start;
+ G_UNLOCK (proxy_drive);
+
+ return res;
+}
+
+static gboolean
+g_proxy_drive_can_stop (GDrive *drive)
+{
+ GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+ gboolean res;
+
+ G_LOCK (proxy_drive);
+ res = proxy_drive->can_stop;
+ G_UNLOCK (proxy_drive);
+
+ return res;
+}
+
+static GDriveStartStopType
+g_proxy_drive_get_start_stop_type (GDrive *drive)
+{
+ GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+ GDriveStartStopType res;
+
+ G_LOCK (proxy_drive);
+ res = proxy_drive->start_stop_type;
+ G_UNLOCK (proxy_drive);
+
+ return res;
+}
+
static char *
g_proxy_drive_get_identifier (GDrive *drive,
const char *kind)
@@ -466,6 +528,8 @@ operation_cancelled (GCancellable *cancellable,
dbus_connection_unref (connection);
}
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
eject_cb (DBusMessage *reply,
GError *error,
@@ -587,6 +651,557 @@ g_proxy_drive_eject_finish (GDrive *drive,
return TRUE;
}
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+stop_cb (DBusMessage *reply,
+ GError *error,
+ DBusOp *data)
+{
+ if (data->cancelled_handler_id > 0)
+ g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+
+ if (!g_cancellable_is_cancelled (data->cancellable))
+ {
+ GSimpleAsyncResult *simple;
+
+ 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->cancellable != NULL)
+ g_object_unref (data->cancellable);
+ g_free (data);
+}
+
+static void
+g_proxy_drive_stop (GDrive *drive,
+ GMountUnmountFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+ DBusConnection *connection;
+ const char *name;
+ DBusMessage *message;
+ DBusOp *data;
+ dbus_uint32_t _flags = flags;
+
+ G_LOCK (proxy_drive);
+
+ 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,
+ _("Operation was cancelled"));
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ G_UNLOCK (proxy_drive);
+ goto out;
+ }
+
+ data = g_new0 (DBusOp, 1);
+ data->drive = g_object_ref (drive);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ if (cancellable != NULL)
+ {
+ 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",
+ "DriveStop");
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING,
+ &(proxy_drive->id),
+ DBUS_TYPE_STRING,
+ &(data->cancellation_id),
+ DBUS_TYPE_UINT32,
+ &_flags,
+ DBUS_TYPE_INVALID);
+ G_UNLOCK (proxy_drive);
+
+ _g_dbus_connection_call_async (connection,
+ message,
+ -1,
+ (GAsyncDBusCallback) stop_cb,
+ data);
+ dbus_connection_unref (connection);
+ dbus_message_unref (message);
+ out:
+ ;
+}
+
+static gboolean
+g_proxy_drive_stop_finish (GDrive *drive,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return FALSE;
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct {
+ GProxyDrive *drive;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+
+ gchar *cancellation_id;
+ GCancellable *cancellable;
+ gulong cancelled_handler_id;
+
+ gchar *start_op_id;
+ GMountOperation *start_operation;
+ gulong reply_handler_id;
+} DBusStartOp;
+
+static void
+start_cb (DBusMessage *reply,
+ GError *error,
+ DBusStartOp *data)
+{
+ if (data->cancelled_handler_id > 0)
+ g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+
+ if (!g_cancellable_is_cancelled (data->cancellable))
+ {
+ GSimpleAsyncResult *simple;
+
+ 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_in_idle (simple);
+ g_object_unref (simple);
+ }
+
+ /* free DBusStartOp */
+ if (strlen (data->start_op_id) > 0)
+ g_hash_table_remove (data->drive->hash_start_op_id_to_data, data->start_op_id);
+ g_object_unref (data->drive);
+
+ g_free (data->start_op_id);
+ if (data->reply_handler_id > 0)
+ g_signal_handler_disconnect (data->start_operation, data->reply_handler_id);
+ if (data->start_operation != NULL)
+ g_object_unref (data->start_operation);
+
+ g_free (data->cancellation_id);
+ if (data->cancellable != NULL)
+ g_object_unref (data->cancellable);
+
+ g_free (data);
+}
+
+static void
+start_cancelled (GCancellable *cancellable,
+ gpointer user_data)
+{
+ DBusStartOp *data = user_data;
+ GSimpleAsyncResult *simple;
+ DBusConnection *connection;
+ DBusMessage *message;
+ const char *name;
+
+ G_LOCK (proxy_drive);
+
+ simple = g_simple_async_result_new_error (G_OBJECT (data->drive),
+ data->callback,
+ data->user_data,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ _("Operation was cancelled"));
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+
+ /* Now tell the remote drive 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_UNLOCK (proxy_drive);
+
+ _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_drive_start (GDrive *drive,
+ GDriveStartFlags flags,
+ GMountOperation *start_operation,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+ DBusStartOp *data;
+ DBusConnection *connection;
+ const char *name;
+ DBusMessage *message;
+
+ G_LOCK (proxy_drive);
+
+ 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,
+ _("Operation was cancelled"));
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ G_UNLOCK (proxy_drive);
+ goto out;
+ }
+
+ data = g_new0 (DBusStartOp, 1);
+ data->drive = g_object_ref (drive);
+ data->callback = callback;
+ data->user_data = user_data;
+ if (cancellable != NULL)
+ {
+ 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 (start_cancelled),
+ data);
+ }
+ else
+ {
+ data->cancellation_id = g_strdup ("");
+ }
+
+ if (start_operation != NULL)
+ {
+ data->start_op_id = g_strdup_printf ("%p", start_operation);
+ data->start_operation = g_object_ref (start_operation);
+ g_hash_table_insert (proxy_drive->hash_start_op_id_to_data,
+ data->start_op_id,
+ data);
+ }
+ else
+ {
+ data->start_op_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",
+ "DriveStart");
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING,
+ &(proxy_drive->id),
+ DBUS_TYPE_STRING,
+ &(data->cancellation_id),
+ DBUS_TYPE_UINT32,
+ &(flags),
+ DBUS_TYPE_STRING,
+ &(data->start_op_id),
+ DBUS_TYPE_INVALID);
+ G_UNLOCK (proxy_drive);
+
+ _g_dbus_connection_call_async (connection,
+ message,
+ G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT,
+ (GAsyncDBusCallback) start_cb,
+ data);
+ dbus_message_unref (message);
+ dbus_connection_unref (connection);
+
+ out:
+ ;
+}
+
+
+static void
+start_op_reply_cb (DBusMessage *reply,
+ GError *error,
+ DBusStartOp *data)
+{
+ if (error != NULL)
+ {
+ g_warning ("Error from StartOpReply(): %s", error->message);
+ }
+}
+
+static void
+start_operation_reply (GMountOperation *start_operation,
+ GMountOperationResult result,
+ gpointer user_data)
+{
+ DBusStartOp *data = user_data;
+ DBusConnection *connection;
+ const char *name;
+ DBusMessage *message;
+ const char *user_name;
+ const char *domain;
+ const char *password;
+ char *encoded_password;
+ dbus_uint32_t password_save;
+ dbus_uint32_t choice;
+ dbus_bool_t anonymous;
+
+ connection = g_proxy_volume_monitor_get_dbus_connection (data->drive->volume_monitor);
+ name = g_proxy_volume_monitor_get_dbus_name (data->drive->volume_monitor);
+
+ user_name = g_mount_operation_get_username (start_operation);
+ domain = g_mount_operation_get_domain (start_operation);
+ password = g_mount_operation_get_password (start_operation);
+ password_save = g_mount_operation_get_password_save (start_operation);
+ choice = g_mount_operation_get_choice (start_operation);
+ anonymous = g_mount_operation_get_anonymous (start_operation);
+
+ if (user_name == NULL)
+ user_name = "";
+ if (domain == NULL)
+ domain = "";
+ if (password == NULL)
+ password = "";
+
+ /* NOTE: this is not to add "security", it's merely to prevent accidental exposure
+ * of passwords when running dbus-monitor
+ */
+ encoded_password = g_base64_encode ((const guchar *) password, (gsize) (strlen (password) + 1));
+
+ message = dbus_message_new_method_call (name,
+ "/org/gtk/Private/RemoteVolumeMonitor",
+ "org.gtk.Private.RemoteVolumeMonitor",
+ "StartOpReply");
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING,
+ &(data->drive->id),
+ DBUS_TYPE_STRING,
+ &(data->start_op_id),
+ DBUS_TYPE_INT32,
+ &result,
+ DBUS_TYPE_STRING,
+ &user_name,
+ DBUS_TYPE_STRING,
+ &domain,
+ DBUS_TYPE_STRING,
+ &encoded_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) start_op_reply_cb,
+ data);
+
+ g_free (encoded_password);
+ dbus_message_unref (message);
+ dbus_connection_unref (connection);
+}
+
+void
+g_proxy_drive_handle_start_op_ask_password (GProxyDrive *drive,
+ DBusMessageIter *iter)
+{
+ const char *start_op_id;
+ const char *message;
+ const char *default_user;
+ const char *default_domain;
+ dbus_int32_t flags;
+ DBusStartOp *data;
+
+ dbus_message_iter_get_basic (iter, &start_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 (drive->hash_start_op_id_to_data, start_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->start_operation,
+ "reply",
+ G_CALLBACK (start_operation_reply),
+ data);
+ }
+
+ g_signal_emit_by_name (data->start_operation,
+ "ask-password",
+ message,
+ default_user,
+ default_domain,
+ flags);
+
+ out:
+ ;
+}
+
+void
+g_proxy_drive_handle_start_op_ask_question (GProxyDrive *drive,
+ DBusMessageIter *iter)
+{
+ const char *start_op_id;
+ const char *message;
+ GPtrArray *choices;
+ DBusMessageIter iter_array;
+ DBusStartOp *data;
+
+ choices = NULL;
+
+ dbus_message_iter_get_basic (iter, &start_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 (drive->hash_start_op_id_to_data, start_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->start_operation,
+ "reply",
+ G_CALLBACK (start_operation_reply),
+ data);
+ }
+
+ g_signal_emit_by_name (data->start_operation,
+ "ask-question",
+ message,
+ choices->pdata);
+
+ out:
+ g_ptr_array_free (choices, TRUE);
+}
+
+void
+g_proxy_drive_handle_start_op_aborted (GProxyDrive *drive,
+ DBusMessageIter *iter)
+{
+ const char *start_op_id;
+ DBusStartOp *data;
+
+ dbus_message_iter_get_basic (iter, &start_op_id);
+ dbus_message_iter_next (iter);
+
+ data = g_hash_table_lookup (drive->hash_start_op_id_to_data, start_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->start_operation, "aborted");
+
+ out:
+ ;
+}
+
+static gboolean
+g_proxy_drive_start_finish (GDrive *drive,
+ GAsyncResult *result,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return FALSE;
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
poll_for_media_cb (DBusMessage *reply,
GError *error,
@@ -703,6 +1318,8 @@ g_proxy_drive_poll_for_media_finish (GDrive *drive,
return TRUE;
}
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
g_proxy_drive_drive_iface_init (GDriveIface *iface)
@@ -722,6 +1339,13 @@ g_proxy_drive_drive_iface_init (GDriveIface *iface)
iface->poll_for_media_finish = g_proxy_drive_poll_for_media_finish;
iface->get_identifier = g_proxy_drive_get_identifier;
iface->enumerate_identifiers = g_proxy_drive_enumerate_identifiers;
+ iface->can_start = g_proxy_drive_can_start;
+ iface->start = g_proxy_drive_start;
+ iface->start_finish = g_proxy_drive_start_finish;
+ iface->can_stop = g_proxy_drive_can_stop;
+ iface->stop = g_proxy_drive_stop;
+ iface->stop_finish = g_proxy_drive_stop_finish;
+ iface->get_start_stop_type = g_proxy_drive_get_start_stop_type;
}
void
diff --git a/monitor/proxy/gproxydrive.h b/monitor/proxy/gproxydrive.h
index 7494286c..ab26244d 100644
--- a/monitor/proxy/gproxydrive.h
+++ b/monitor/proxy/gproxydrive.h
@@ -50,6 +50,15 @@ void g_proxy_drive_update (GProxyDrive *drive,
DBusMessageIter *iter);
const char *g_proxy_drive_get_id (GProxyDrive *drive);
+void g_proxy_drive_handle_start_op_ask_password (GProxyDrive *drive,
+ DBusMessageIter *iter);
+
+void g_proxy_drive_handle_start_op_ask_question (GProxyDrive *drive,
+ DBusMessageIter *iter);
+
+void g_proxy_drive_handle_start_op_aborted (GProxyDrive *drive,
+ DBusMessageIter *iter);
+
G_END_DECLS
#endif /* __G_PROXY_DRIVE_H__ */
diff --git a/monitor/proxy/gproxyvolume.c b/monitor/proxy/gproxyvolume.c
index fb3402a0..30c717f2 100644
--- a/monitor/proxy/gproxyvolume.c
+++ b/monitor/proxy/gproxyvolume.c
@@ -936,7 +936,7 @@ g_proxy_volume_mount (GVolume *volume,
_g_dbus_connection_call_async (connection,
message,
- 30 * 60 * 1000, /* 30 minute timeout */
+ G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT, /* 30 minute timeout */
(GAsyncDBusCallback) mount_cb,
data);
dbus_message_unref (message);
diff --git a/monitor/proxy/gproxyvolumemonitor.c b/monitor/proxy/gproxyvolumemonitor.c
index aa79460c..3b5a25d2 100644
--- a/monitor/proxy/gproxyvolumemonitor.c
+++ b/monitor/proxy/gproxyvolumemonitor.c
@@ -639,7 +639,11 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
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_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEjectButton") ||
+ dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveStopButton") ||
+ dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpAskPassword") ||
+ dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpAskQuestion") ||
+ dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpAborted"))
{
dbus_message_iter_init (message, &iter);
@@ -693,6 +697,33 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
signal_emit_in_idle (monitor, "drive-eject-button", drive);
}
}
+ else if (strcmp (member, "DriveStopButton") == 0)
+ {
+ drive = g_hash_table_lookup (monitor->drives, id);
+ if (drive != NULL)
+ {
+ signal_emit_in_idle (drive, "stop-button", NULL);
+ signal_emit_in_idle (monitor, "drive-stop-button", drive);
+ }
+ }
+ else if (strcmp (member, "StartOpAskPassword") == 0)
+ {
+ drive = g_hash_table_lookup (monitor->drives, id);
+ if (drive != NULL)
+ g_proxy_drive_handle_start_op_ask_password (drive, &iter);
+ }
+ else if (strcmp (member, "StartOpAskQuestion") == 0)
+ {
+ drive = g_hash_table_lookup (monitor->drives, id);
+ if (drive != NULL)
+ g_proxy_drive_handle_start_op_ask_question (drive, &iter);
+ }
+ else if (strcmp (member, "StartOpAborted") == 0)
+ {
+ drive = g_hash_table_lookup (monitor->drives, id);
+ if (drive != NULL)
+ g_proxy_drive_handle_start_op_aborted (drive, &iter);
+ }
}
else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeChanged") ||
diff --git a/monitor/proxy/gproxyvolumemonitor.h b/monitor/proxy/gproxyvolumemonitor.h
index 96b9d841..3d29d7b2 100644
--- a/monitor/proxy/gproxyvolumemonitor.h
+++ b/monitor/proxy/gproxyvolumemonitor.h
@@ -41,6 +41,14 @@ G_BEGIN_DECLS
typedef struct _GProxyVolumeMonitor GProxyVolumeMonitor;
typedef struct _GProxyVolumeMonitorClass GProxyVolumeMonitorClass;
+/* Timeout used for D-Bus messages in msec - this needs to be high enough
+ * to ensure that the user has time to interact with e.g. mount operation
+ * dialogs.
+ *
+ * We use 30 minutes.
+ */
+#define G_PROXY_VOLUME_MONITOR_DBUS_TIMEOUT 30*60*1000
+
/* Forward definitions */
typedef struct _GProxyDrive GProxyDrive;
typedef struct _GProxyVolume GProxyVolume;
diff --git a/monitor/proxy/gvfsproxyvolumemonitordaemon.c b/monitor/proxy/gvfsproxyvolumemonitordaemon.c
index e59374db..d066672b 100644
--- a/monitor/proxy/gvfsproxyvolumemonitordaemon.c
+++ b/monitor/proxy/gvfsproxyvolumemonitordaemon.c
@@ -205,10 +205,13 @@ static void monitor_try_create (void);
* boolean has-media
* boolean is-media-removable
* boolean is-media-check-automatic
+ * boolean can-start
+ * boolean can-stop
+ * uint32 start-stop-type
* array:string volume-ids
* dict:string->string identifiers
*/
-#define DRIVE_STRUCT_TYPE "(sssbbbbbasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbbbbuasa{ss})"
static void
append_drive (GDrive *drive, DBusMessageIter *iter_array)
@@ -225,6 +228,9 @@ append_drive (GDrive *drive, DBusMessageIter *iter_array)
gboolean has_media;
gboolean is_media_removable;
gboolean is_media_check_automatic;
+ gboolean can_start;
+ gboolean can_stop;
+ GDriveStartStopType start_stop_type;
GList *volumes, *l;
char **identifiers;
int n;
@@ -243,6 +249,9 @@ append_drive (GDrive *drive, DBusMessageIter *iter_array)
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);
+ can_start = g_drive_can_start (drive);
+ can_stop = g_drive_can_stop (drive);
+ start_stop_type = g_drive_get_start_stop_type (drive);
volumes = g_drive_get_volumes (drive);
identifiers = g_drive_enumerate_identifiers (drive);
@@ -257,6 +266,9 @@ append_drive (GDrive *drive, DBusMessageIter *iter_array)
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_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_start);
+ dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_stop);
+ dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_UINT32, &start_stop_type);
dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_volume_array);
for (l = volumes; l != NULL; l = l->next)
@@ -1192,6 +1204,530 @@ handle_drive_eject (DBusConnection *connection, DBusMessage *message)
/* ---------------------------------------------------------------------------------------------------- */
+static DBusHandlerResult
+handle_start_op_reply (DBusConnection *connection, DBusMessage *message)
+{
+ const char *id;
+ const char *start_op_id;
+ dbus_int32_t result;
+ const char *user_name;
+ const char *domain;
+ const char *encoded_password;
+ char *decoded_password;
+ gsize decoded_password_len;
+ dbus_int32_t password_save;
+ dbus_int32_t choice;
+ dbus_bool_t anonymous;
+ DBusError dbus_error;
+ DBusHandlerResult ret;
+ GList *drives, *l;
+ GDrive *drive;
+ DBusMessage *reply;
+ GMountOperation *start_operation;
+
+ drives = NULL;
+ decoded_password = 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, &start_op_id,
+ DBUS_TYPE_INT32, &result,
+ DBUS_TYPE_STRING, &user_name,
+ DBUS_TYPE_STRING, &domain,
+ DBUS_TYPE_STRING, &encoded_password,
+ DBUS_TYPE_INT32, &password_save,
+ DBUS_TYPE_INT32, &choice,
+ DBUS_TYPE_BOOLEAN, &anonymous,
+ DBUS_TYPE_INVALID))
+ {
+ g_warning ("Error parsing args for StartOpReply(): %s: %s", dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto out;
+ }
+
+ print_debug ("in handle_start_op_reply");
+
+ ret = DBUS_HANDLER_RESULT_HANDLED;
+
+ drive = NULL;
+ drives = g_volume_monitor_get_connected_drives (monitor);
+ for (l = drives; l != NULL; l = l->next)
+ {
+ char *drive_id;
+
+ drive = G_DRIVE (l->data);
+ drive_id = g_strdup_printf ("%p", drive);
+ if (strcmp (drive_id, id) == 0)
+ break;
+
+ g_free (drive_id);
+ }
+ if (l == NULL)
+ drive = NULL;
+
+ if (drive == NULL)
+ {
+ DBusMessage *reply;
+ reply = dbus_message_new_error (message,
+ "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+ "The given drive was not found");
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+ goto out;
+ }
+
+ start_operation = g_object_get_data (G_OBJECT (drive), "start_operation");
+ if (start_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;
+ }
+
+ decoded_password = (gchar *) g_base64_decode (encoded_password, &decoded_password_len);
+
+ g_mount_operation_set_username (start_operation, user_name);
+ g_mount_operation_set_domain (start_operation, domain);
+ g_mount_operation_set_password (start_operation, decoded_password);
+ g_mount_operation_set_password_save (start_operation, password_save);
+ g_mount_operation_set_choice (start_operation, choice);
+ g_mount_operation_set_anonymous (start_operation, anonymous);
+
+ g_mount_operation_reply (start_operation, result);
+
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+
+ out:
+ g_free (decoded_password);
+ if (drives != NULL)
+ {
+ g_list_foreach (drives, (GFunc) g_object_unref, NULL);
+ g_list_free (drives);
+ }
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+drive_stop_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
+{
+ GError *error;
+ DBusMessage *reply;
+
+ print_debug ("in drive_stop_cb");
+
+ g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+
+ error = NULL;
+ if (!g_drive_stop_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);
+ }
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (message);
+ dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+handle_drive_stop (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;
+ GDrive *drive;
+ 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))
+ {
+ g_warning ("Error parsing args for DriveStop(): %s: %s", dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto out;
+ }
+
+ print_debug ("in handle_drive_stop");
+
+ 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)
+ {
+ char *drive_id;
+
+ drive = G_DRIVE (l->data);
+ drive_id = g_strdup_printf ("%p", drive);
+ if (strcmp (drive_id, id) == 0)
+ break;
+
+ g_free (drive_id);
+ }
+ if (l == NULL)
+ drive = NULL;
+
+ if (drive == NULL)
+ {
+ DBusMessage *reply;
+ reply = dbus_message_new_error (message,
+ "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+ "The given drive was not found");
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+ 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_stop (drive,
+ unmount_flags,
+ cancellable,
+ (GAsyncReadyCallback) drive_stop_cb,
+ dbus_message_ref (message));
+
+ out:
+ if (drives != NULL)
+ {
+ g_list_foreach (drives, (GFunc) g_object_unref, NULL);
+ g_list_free (drives);
+ }
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+drive_start_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
+{
+ GError *error;
+ DBusMessage *reply;
+
+ print_debug ("in drive_start_cb");
+
+ g_object_set_data (G_OBJECT (drive), "start_operation", NULL);
+ g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+
+ error = NULL;
+ if (!g_drive_start_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);
+ }
+
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (message);
+ dbus_message_unref (reply);
+}
+
+static void
+start_ask_password_cb (GMountOperation *start_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;
+ GDrive *drive;
+ const gchar *start_op_id;
+ const gchar *start_op_owner;
+
+ print_debug ("in ask_password_cb %s", message_to_show);
+
+ drive = G_DRIVE (user_data);
+
+ id = g_strdup_printf ("%p", drive);
+
+ start_op_id = g_object_get_data (G_OBJECT (start_operation), "start_op_id");
+ start_op_owner = g_object_get_data (G_OBJECT (start_operation), "start_op_owner");
+
+ message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+ "org.gtk.Private.RemoteVolumeMonitor",
+ "StartOpAskPassword");
+ 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, &start_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, start_op_owner);
+
+ dbus_connection_send (connection, message, NULL);
+ dbus_message_unref (message);
+
+ g_free (id);
+}
+
+static void
+start_ask_question_cb (GMountOperation *start_operation,
+ const gchar *message_to_show,
+ gchar **choices,
+ gpointer user_data)
+{
+ gchar *id;
+ DBusMessage *message;
+ DBusMessageIter iter;
+ DBusMessageIter iter_string_array;
+ const gchar *start_op_id;
+ const gchar *start_op_owner;
+ GDrive *drive;
+ guint n;
+
+ print_debug ("in ask_question_cb %s", message_to_show);
+
+ drive = G_DRIVE (user_data);
+
+ id = g_strdup_printf ("%p", drive);
+
+ start_op_id = g_object_get_data (G_OBJECT (start_operation), "start_op_id");
+ start_op_owner = g_object_get_data (G_OBJECT (start_operation), "start_op_owner");
+
+ message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+ "org.gtk.Private.RemoteVolumeMonitor",
+ "StartOpAskQuestion");
+ 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, &start_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, start_op_owner);
+
+ dbus_connection_send (connection, message, NULL);
+ dbus_message_unref (message);
+
+ g_free (id);
+}
+
+static void
+start_aborted_cb (GMountOperation *start_operation,
+ gpointer user_data)
+{
+ gchar *id;
+ DBusMessage *message;
+ DBusMessageIter iter;
+ GDrive *drive;
+ const gchar *start_op_id;
+ const gchar *start_op_owner;
+
+ print_debug ("in aborted_cb");
+
+ drive = G_DRIVE (user_data);
+
+ id = g_strdup_printf ("%p", drive);
+
+ start_op_id = g_object_get_data (G_OBJECT (start_operation), "start_op_id");
+ start_op_owner = g_object_get_data (G_OBJECT (start_operation), "start_op_owner");
+
+ message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+ "org.gtk.Private.RemoteVolumeMonitor",
+ "StartOpAborted");
+ 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, &start_op_id);
+
+ dbus_message_set_destination (message, start_op_owner);
+
+ dbus_connection_send (connection, message, NULL);
+ dbus_message_unref (message);
+
+ g_free (id);
+}
+
+static DBusHandlerResult
+handle_drive_start (DBusConnection *connection, DBusMessage *message)
+{
+ const char *id;
+ const char *cancellation_id;
+ const char *sender;
+ const char *start_op_id;
+ GDriveStartFlags flags;
+ DBusError dbus_error;
+ GList *drives, *l;
+ GDrive *drive;
+ DBusHandlerResult ret;
+ GMountOperation *start_operation;
+ GCancellable *cancellable;
+
+ 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_UINT32, &flags,
+ DBUS_TYPE_STRING, &start_op_id,
+ DBUS_TYPE_INVALID))
+ {
+ g_warning ("Error parsing args for DriveStart(): %s: %s", dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto out;
+ }
+
+ print_debug ("in handle_drive_start");
+
+ 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)
+ {
+ char *drive_id;
+
+ drive = G_DRIVE (l->data);
+ drive_id = g_strdup_printf ("%p", drive);
+ if (strcmp (drive_id, id) == 0)
+ break;
+
+ g_free (drive_id);
+ }
+ if (l == NULL)
+ drive = NULL;
+
+ if (drive == NULL)
+ {
+ DBusMessage *reply;
+ reply = dbus_message_new_error (message,
+ "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+ "The given drive was not found");
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+ 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;
+ }
+
+ start_operation = NULL;
+ if (start_op_id != NULL && strlen (start_op_id) > 0)
+ {
+ start_operation = g_proxy_mount_operation_new ();
+ g_signal_connect (start_operation, "ask-password", G_CALLBACK (start_ask_password_cb), drive);
+ g_signal_connect (start_operation, "ask-question", G_CALLBACK (start_ask_question_cb), drive);
+ g_signal_connect (start_operation, "aborted", G_CALLBACK (start_aborted_cb), drive);
+ g_object_set_data_full (G_OBJECT (start_operation), "start_op_id", g_strdup (start_op_id), g_free);
+ g_object_set_data_full (G_OBJECT (start_operation), "start_op_owner", g_strdup (sender), g_free);
+ g_object_set_data_full (G_OBJECT (drive), "start_operation", start_operation, g_object_unref);
+ }
+
+ 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_start (drive,
+ flags,
+ start_operation,
+ cancellable,
+ (GAsyncReadyCallback) drive_start_cb,
+ dbus_message_ref (message));
+
+ out:
+ if (drives != NULL)
+ {
+ g_list_foreach (drives, (GFunc) g_object_unref, NULL);
+ g_list_free (drives);
+ }
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static void
drive_poll_for_media_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
{
@@ -1493,6 +2029,15 @@ filter_function (DBusConnection *connection, DBusMessage *message, void *user_da
else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DrivePollForMedia"))
ret = handle_drive_poll_for_media (connection, message);
+ else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveStart"))
+ ret = handle_drive_start (connection, message);
+
+ else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveStop"))
+ ret = handle_drive_stop (connection, message);
+
+ else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "StartOpReply"))
+ ret = handle_start_op_reply (connection, message);
+
}
}
}
@@ -1548,11 +2093,16 @@ drive_disconnected (GVolumeMonitor *monitor, GDrive *drive, DBusConnection *conn
static void
drive_eject_button (GVolumeMonitor *monitor, GDrive *drive, DBusConnection *connection)
{
- g_warning ("drive eject button!");
emit_signal (connection, "DriveEjectButton", drive, (AppendFunc) append_drive);
}
static void
+drive_stop_button (GVolumeMonitor *monitor, GDrive *drive, DBusConnection *connection)
+{
+ emit_signal (connection, "DriveStopButton", drive, (AppendFunc) append_drive);
+}
+
+static void
volume_changed (GVolumeMonitor *monitor, GVolume *volume, DBusConnection *connection)
{
emit_signal (connection, "VolumeChanged", volume, (AppendFunc) append_volume);
@@ -1734,6 +2284,7 @@ g_vfs_proxy_volume_monitor_daemon_main (int argc,
g_signal_connect (monitor, "drive-connected", (GCallback) drive_connected, connection);
g_signal_connect (monitor, "drive-disconnected", (GCallback) drive_disconnected, connection);
g_signal_connect (monitor, "drive-eject-button", (GCallback) drive_eject_button, connection);
+ g_signal_connect (monitor, "drive-stop-button", (GCallback) drive_stop_button, connection);
g_signal_connect (monitor, "volume-changed", (GCallback) volume_changed, connection);
g_signal_connect (monitor, "volume-added", (GCallback) volume_added, connection);