diff options
author | David Zeuthen <davidz@redhat.com> | 2009-06-17 09:49:44 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2009-06-17 09:50:35 -0400 |
commit | 4053577a19585c63e23e6c6e85011e2df9910745 (patch) | |
tree | 8c77e6ab423827a8623e7b55ad77ef362eb669af /monitor | |
parent | 8a72222d454bdf12e88402b2f012667e7c8e9754 (diff) | |
download | gvfs-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.c | 676 | ||||
-rw-r--r-- | monitor/gdu/ggduvolumemonitor.c | 57 | ||||
-rw-r--r-- | monitor/proxy/gproxydrive.c | 626 | ||||
-rw-r--r-- | monitor/proxy/gproxydrive.h | 9 | ||||
-rw-r--r-- | monitor/proxy/gproxyvolume.c | 2 | ||||
-rw-r--r-- | monitor/proxy/gproxyvolumemonitor.c | 33 | ||||
-rw-r--r-- | monitor/proxy/gproxyvolumemonitor.h | 8 | ||||
-rw-r--r-- | monitor/proxy/gvfsproxyvolumemonitordaemon.c | 555 |
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), °raded)) + 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); |