summaryrefslogtreecommitdiff
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
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.
-rw-r--r--client/gdaemonfile.c93
-rw-r--r--client/gvfsdaemondbus.c15
-rw-r--r--common/gmountoperationdbus.c12
-rw-r--r--common/gmountsource.c6
-rw-r--r--common/gvfsdaemonprotocol.h2
-rw-r--r--daemon/Makefile.am2
-rw-r--r--daemon/gvfsbackend.c10
-rw-r--r--daemon/gvfsbackend.h19
-rw-r--r--daemon/gvfsbackendcomputer.c162
-rw-r--r--daemon/gvfsjobstartmountable.c168
-rw-r--r--daemon/gvfsjobstartmountable.h64
-rw-r--r--daemon/gvfsjobstopmountable.c164
-rw-r--r--daemon/gvfsjobstopmountable.h64
-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
-rw-r--r--programs/gvfs-mount.c16
22 files changed, 2587 insertions, 176 deletions
diff --git a/client/gdaemonfile.c b/client/gdaemonfile.c
index 0071a5bd..2da81f43 100644
--- a/client/gdaemonfile.c
+++ b/client/gdaemonfile.c
@@ -1326,6 +1326,95 @@ g_daemon_file_mount_mountable_finish (GFile *file,
}
static void
+start_mountable_async_cb (DBusMessage *reply,
+ DBusConnection *connection,
+ GSimpleAsyncResult *result,
+ GCancellable *cancellable,
+ gpointer callback_data)
+{
+ g_simple_async_result_complete (result);
+}
+
+static void
+g_daemon_file_start_mountable (GFile *file,
+ GDriveStartFlags flags,
+ GMountOperation *start_operation,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GMountSource *mount_source;
+ const char *dbus_id, *obj_path;
+
+ mount_source = g_mount_operation_dbus_wrap (start_operation, _g_daemon_vfs_get_async_bus ());
+
+ dbus_id = g_mount_source_get_dbus_id (mount_source);
+ obj_path = g_mount_source_get_obj_path (mount_source);
+
+ if (start_operation)
+ g_object_ref (start_operation);
+
+ do_async_path_call (file,
+ G_VFS_DBUS_MOUNT_OP_START_MOUNTABLE,
+ cancellable,
+ callback, user_data,
+ start_mountable_async_cb,
+ start_operation, start_operation ? g_object_unref : NULL,
+ DBUS_TYPE_STRING, &dbus_id,
+ DBUS_TYPE_OBJECT_PATH, &obj_path,
+ 0);
+
+ g_object_unref (mount_source);
+}
+
+static gboolean
+g_daemon_file_start_mountable_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ return TRUE;
+}
+
+static void
+stop_mountable_async_cb (DBusMessage *reply,
+ DBusConnection *connection,
+ GSimpleAsyncResult *result,
+ GCancellable *cancellable,
+ gpointer callback_data)
+{
+ g_simple_async_result_complete (result);
+}
+
+static void
+g_daemon_file_stop_mountable (GFile *file,
+ GMountUnmountFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ guint32 dbus_flags;
+
+ dbus_flags = flags;
+ do_async_path_call (file,
+ G_VFS_DBUS_MOUNT_OP_STOP_MOUNTABLE,
+ cancellable,
+ callback, user_data,
+ stop_mountable_async_cb,
+ NULL, NULL,
+ DBUS_TYPE_UINT32, &dbus_flags,
+ 0);
+}
+
+static gboolean
+g_daemon_file_stop_mountable_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ return TRUE;
+}
+
+
+static void
eject_mountable_async_cb (DBusMessage *reply,
DBusConnection *connection,
GSimpleAsyncResult *result,
@@ -2787,6 +2876,10 @@ g_daemon_file_file_iface_init (GFileIface *iface)
iface->make_symbolic_link = g_daemon_file_make_symbolic_link;
iface->monitor_dir = g_daemon_file_monitor_dir;
iface->monitor_file = g_daemon_file_monitor_file;
+ iface->start_mountable = g_daemon_file_start_mountable;
+ iface->start_mountable_finish = g_daemon_file_start_mountable_finish;
+ iface->stop_mountable = g_daemon_file_stop_mountable;
+ iface->stop_mountable_finish = g_daemon_file_stop_mountable_finish;
/* Async operations */
diff --git a/client/gvfsdaemondbus.c b/client/gvfsdaemondbus.c
index c3df2cf6..a9dae139 100644
--- a/client/gvfsdaemondbus.c
+++ b/client/gvfsdaemondbus.c
@@ -508,10 +508,19 @@ async_call_send (AsyncDBusCall *async_call)
{
AsyncCallCancelData *cancel_data;
+ _g_dbus_connection_call_async (async_call->connection,
+ async_call->message,
+ G_VFS_DBUS_TIMEOUT_MSECS,
+ async_dbus_response,
+ async_call);
+
if (async_call->cancellable)
{
cancel_data = g_new0 (AsyncCallCancelData, 1);
cancel_data->connection = dbus_connection_ref (async_call->connection);
+ /* make sure we get the serial *after* the message has been sent, otherwise
+ * it will be 0
+ */
cancel_data->serial = dbus_message_get_serial (async_call->message);
async_call->cancelled_tag =
g_signal_connect_data (async_call->cancellable, "cancelled",
@@ -520,12 +529,6 @@ async_call_send (AsyncDBusCall *async_call)
(GClosureNotify)async_call_cancel_data_free,
0);
}
-
- _g_dbus_connection_call_async (async_call->connection,
- async_call->message,
- G_VFS_DBUS_TIMEOUT_MSECS,
- async_dbus_response,
- async_call);
}
static void
diff --git a/common/gmountoperationdbus.c b/common/gmountoperationdbus.c
index 7700c073..b8ac4f7d 100644
--- a/common/gmountoperationdbus.c
+++ b/common/gmountoperationdbus.c
@@ -281,10 +281,8 @@ mount_op_ask_question (GMountOperationDBus *op_dbus,
const char *message_string;
char **choices;
int num_choices;
- dbus_bool_t handled = FALSE;
DBusMessage *reply;
DBusError error;
- gboolean res;
DBusMessageIter iter;
reply = NULL;
@@ -316,15 +314,7 @@ mount_op_ask_question (GMountOperationDBus *op_dbus,
g_signal_emit_by_name (op_dbus->op, "ask_question",
message_string,
- choices,
- &res);
- if (!res)
- {
- _g_dbus_message_append_args (reply,
- DBUS_TYPE_BOOLEAN, &handled,
- 0);
- mount_op_send_reply (op_dbus, reply);
- }
+ choices);
dbus_free_string_array (choices);
}
diff --git a/common/gmountsource.c b/common/gmountsource.c
index b2f490d1..ac705fad 100644
--- a/common/gmountsource.c
+++ b/common/gmountsource.c
@@ -613,7 +613,7 @@ g_mount_source_ask_question_async (GMountSource *source,
_g_dbus_message_append_args (message,
DBUS_TYPE_STRING, &message_string,
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
- choices, n_choices,
+ &choices, n_choices,
0);
result = g_simple_async_result_new (G_OBJECT (source), callback, user_data,
@@ -687,13 +687,12 @@ static gboolean
op_ask_question (GMountOperation *op,
const char *message,
const char **choices,
- gint n_choices,
GMountSource *mount_source)
{
g_mount_source_ask_question_async (mount_source,
message,
choices,
- n_choices,
+ g_strv_length ((gchar **) choices),
op_ask_question_reply,
g_object_ref (op));
g_signal_stop_emission_by_name (op, "ask_question");
@@ -741,7 +740,6 @@ g_mount_source_get_operation (GMountSource *mount_source)
g_object_ref (mount_source),
g_object_unref);
-
g_signal_connect (op, "ask_password", (GCallback)op_ask_password, mount_source);
g_signal_connect (op, "ask_question", (GCallback)op_ask_question, mount_source);
g_signal_connect (op, "aborted", (GCallback)op_aborted, mount_source);
diff --git a/common/gvfsdaemonprotocol.h b/common/gvfsdaemonprotocol.h
index 5f70ed3d..ce8b11ea 100644
--- a/common/gvfsdaemonprotocol.h
+++ b/common/gvfsdaemonprotocol.h
@@ -37,6 +37,8 @@ G_BEGIN_DECLS
#define G_VFS_DBUS_MOUNT_OP_MOUNT_MOUNTABLE "MountMountable"
#define G_VFS_DBUS_MOUNT_OP_UNMOUNT_MOUNTABLE "UnountMountable"
#define G_VFS_DBUS_MOUNT_OP_EJECT_MOUNTABLE "EjectMountable"
+#define G_VFS_DBUS_MOUNT_OP_START_MOUNTABLE "StartMountable"
+#define G_VFS_DBUS_MOUNT_OP_STOP_MOUNTABLE "StopMountable"
#define G_VFS_DBUS_MOUNT_OP_SET_DISPLAY_NAME "SetDisplayName"
#define G_VFS_DBUS_MOUNT_OP_DELETE "Delete"
#define G_VFS_DBUS_MOUNT_OP_TRASH "Trash"
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 9761eff6..ae247712 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -124,6 +124,8 @@ libdaemon_la_SOURCES = \
gvfsjobunmount.c gvfsjobunmount.h \
gvfsjobmountmountable.c gvfsjobmountmountable.h \
gvfsjobunmountmountable.c gvfsjobunmountmountable.h \
+ gvfsjobstartmountable.c gvfsjobstartmountable.h \
+ gvfsjobstopmountable.c gvfsjobstopmountable.h \
gvfsjobopenforread.c gvfsjobopenforread.h \
gvfsjobopeniconforread.c gvfsjobopeniconforread.h \
gvfsjobread.c gvfsjobread.h \
diff --git a/daemon/gvfsbackend.c b/daemon/gvfsbackend.c
index 74aabdb2..3be3cdbf 100644
--- a/daemon/gvfsbackend.c
+++ b/daemon/gvfsbackend.c
@@ -46,6 +46,8 @@
#include <gvfsjobunmount.h>
#include <gvfsjobmountmountable.h>
#include <gvfsjobunmountmountable.h>
+#include <gvfsjobstartmountable.h>
+#include <gvfsjobstopmountable.h>
#include <gvfsjobmakedirectory.h>
#include <gvfsjobmakesymlink.h>
#include <gvfsjobcreatemonitor.h>
@@ -533,6 +535,14 @@ backend_dbus_handler (DBusConnection *connection,
job = g_vfs_job_unmount_mountable_new (connection, message, backend, TRUE);
else if (dbus_message_is_method_call (message,
G_VFS_DBUS_MOUNT_INTERFACE,
+ G_VFS_DBUS_MOUNT_OP_START_MOUNTABLE))
+ job = g_vfs_job_start_mountable_new (connection, message, backend);
+ else if (dbus_message_is_method_call (message,
+ G_VFS_DBUS_MOUNT_INTERFACE,
+ G_VFS_DBUS_MOUNT_OP_STOP_MOUNTABLE))
+ job = g_vfs_job_stop_mountable_new (connection, message, backend);
+ else if (dbus_message_is_method_call (message,
+ G_VFS_DBUS_MOUNT_INTERFACE,
G_VFS_DBUS_MOUNT_OP_SET_DISPLAY_NAME))
job = g_vfs_job_set_display_name_new (connection, message, backend);
else if (dbus_message_is_method_call (message,
diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h
index f2bed486..ee68bd86 100644
--- a/daemon/gvfsbackend.h
+++ b/daemon/gvfsbackend.h
@@ -47,6 +47,8 @@ typedef struct _GVfsJobMount GVfsJobMount;
typedef struct _GVfsJobUnmount GVfsJobUnmount;
typedef struct _GVfsJobMountMountable GVfsJobMountMountable;
typedef struct _GVfsJobUnmountMountable GVfsJobUnmountMountable;
+typedef struct _GVfsJobStartMountable GVfsJobStartMountable;
+typedef struct _GVfsJobStopMountable GVfsJobStopMountable;
typedef struct _GVfsJobOpenForRead GVfsJobOpenForRead;
typedef struct _GVfsJobOpenIconForRead GVfsJobOpenIconForRead;
typedef struct _GVfsJobSeekRead GVfsJobSeekRead;
@@ -415,6 +417,23 @@ struct _GVfsBackendClass
gboolean (*try_query_writable_namespaces) (GVfsBackend *backend,
GVfsJobQueryAttributes *job,
const char *filename);
+
+ void (*start_mountable) (GVfsBackend *backend,
+ GVfsJobStartMountable *job,
+ const char *filename,
+ GMountSource *mount_source);
+ gboolean (*try_start_mountable) (GVfsBackend *backend,
+ GVfsJobStartMountable *job,
+ const char *filename,
+ GMountSource *mount_source);
+ void (*stop_mountable) (GVfsBackend *backend,
+ GVfsJobStopMountable *job,
+ const char *filename,
+ GMountUnmountFlags flags);
+ gboolean (*try_stop_mountable) (GVfsBackend *backend,
+ GVfsJobStopMountable *job,
+ const char *filename,
+ GMountUnmountFlags flags);
};
GType g_vfs_backend_get_type (void) G_GNUC_CONST;
diff --git a/daemon/gvfsbackendcomputer.c b/daemon/gvfsbackendcomputer.c
index c0d355f5..9783df6c 100644
--- a/daemon/gvfsbackendcomputer.c
+++ b/daemon/gvfsbackendcomputer.c
@@ -21,7 +21,6 @@
* Cosimo Cecchi <cosimoc@gnome.org>
*/
-
#include <config.h>
#include <sys/types.h>
@@ -66,6 +65,9 @@ typedef struct {
gboolean can_mount;
gboolean can_unmount;
gboolean can_eject;
+ gboolean can_start;
+ gboolean can_stop;
+ GDriveStartStopType start_stop_type;
GDrive *drive;
GVolume *volume;
@@ -133,7 +135,10 @@ computer_file_equal (ComputerFile *a,
if (a->can_mount != b->can_mount ||
a->can_unmount != b->can_unmount ||
- a->can_eject != b->can_eject)
+ a->can_eject != b->can_eject ||
+ a->can_start != b->can_start ||
+ a->can_stop != b->can_stop ||
+ a->start_stop_type != b->start_stop_type)
return FALSE;
return TRUE;
@@ -458,6 +463,11 @@ recompute_files (GVfsBackendComputer *backend)
if (file->drive)
{
+ file->can_start = g_drive_can_start (file->drive);
+ file->can_stop = g_drive_can_stop (file->drive);
+ file->start_stop_type = g_drive_get_start_stop_type (file->drive);
+ if (file->can_start)
+ file->can_mount = FALSE;
basename = g_drive_get_name (file->drive);
extension = ".drive";
}
@@ -650,6 +660,9 @@ file_info_from_file (ComputerFile *file,
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, file->can_mount);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, file->can_unmount);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT, file->can_eject);
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START, file->can_start);
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP, file->can_stop);
+ g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE, file->start_stop_type);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
@@ -781,8 +794,6 @@ mount_volume_cb (GObject *source_object,
volume = G_VOLUME (source_object);
- /* TODO: We're leaking the GMountOperation here */
-
error = NULL;
if (g_volume_mount_finish (volume, res, &error))
{
@@ -910,6 +921,8 @@ try_mount_mountable (GVfsBackend *backend,
if (file->volume)
{
mount_op = g_mount_source_get_operation (mount_source);
+ /* free mount_op when job is completed */
+ g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-mount-op", mount_op, g_object_unref);
g_volume_mount (file->volume,
0,
mount_op,
@@ -1133,6 +1146,145 @@ try_eject_mountable (GVfsBackend *backend,
return TRUE;
}
+
+static void
+drive_start_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVfsJobStartMountable *job = user_data;
+ GError *error;
+ GDrive *drive;
+
+ drive = G_DRIVE (source_object);
+
+ error = NULL;
+ if (g_drive_start_finish (drive, res, &error))
+ {
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+try_start_mountable (GVfsBackend *backend,
+ GVfsJobStartMountable *job,
+ const char *filename,
+ GMountSource *mount_source)
+{
+ ComputerFile *file;
+ GMountOperation *start_op;
+
+ file = lookup (G_VFS_BACKEND_COMPUTER (backend),
+ G_VFS_JOB (job), filename);
+
+ if (file == &root)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_NOT_MOUNTABLE_FILE,
+ _("Not a mountable file"));
+ }
+ else if (file != NULL)
+ {
+ if (file->drive != NULL)
+ {
+ start_op = g_mount_source_get_operation (mount_source);
+ /* free start_op when job is completed */
+ g_object_set_data_full (G_OBJECT (job), "gvfs-backend-computer-start-op", start_op, g_object_unref);
+ g_drive_start (file->drive,
+ 0,
+ start_op,
+ G_VFS_JOB (job)->cancellable,
+ drive_start_cb,
+ job);
+ }
+ else
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Can't start file"));
+ }
+ }
+ else
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Can't start file"));
+ }
+ return TRUE;
+}
+
+
+static void
+drive_stop_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVfsJobStopMountable *job = user_data;
+ GError *error;
+ GDrive *drive;
+
+ drive = G_DRIVE (source_object);
+
+ error = NULL;
+ if (g_drive_stop_finish (drive, res, &error))
+ {
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ }
+ else
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+try_stop_mountable (GVfsBackend *backend,
+ GVfsJobStopMountable *job,
+ const char *filename,
+ GMountUnmountFlags flags)
+{
+ ComputerFile *file;
+
+ file = lookup (G_VFS_BACKEND_COMPUTER (backend),
+ G_VFS_JOB (job), filename);
+
+ if (file == &root)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_NOT_MOUNTABLE_FILE,
+ _("Not a mountable file"));
+ }
+ else if (file != NULL)
+ {
+ if (file->drive != NULL)
+ {
+ g_drive_stop (file->drive,
+ flags,
+ G_VFS_JOB (job)->cancellable,
+ drive_stop_cb,
+ job);
+ }
+ else
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Can't stop file"));
+ }
+ }
+ else
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Can't stop file"));
+ }
+ return TRUE;
+}
+
static void
g_vfs_backend_computer_class_init (GVfsBackendComputerClass *klass)
{
@@ -1149,4 +1301,6 @@ g_vfs_backend_computer_class_init (GVfsBackendComputerClass *klass)
backend_class->try_mount_mountable = try_mount_mountable;
backend_class->try_unmount_mountable = try_unmount_mountable;
backend_class->try_eject_mountable = try_eject_mountable;
+ backend_class->try_start_mountable = try_start_mountable;
+ backend_class->try_stop_mountable = try_stop_mountable;
}
diff --git a/daemon/gvfsjobstartmountable.c b/daemon/gvfsjobstartmountable.c
new file mode 100644
index 00000000..508f7f97
--- /dev/null
+++ b/daemon/gvfsjobstartmountable.c
@@ -0,0 +1,168 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <glib/gi18n.h>
+#include "gvfsjobstartmountable.h"
+#include "gdbusutils.h"
+#include "gvfsdaemonutils.h"
+
+G_DEFINE_TYPE (GVfsJobStartMountable, g_vfs_job_start_mountable, G_VFS_TYPE_JOB_DBUS)
+
+static void run (GVfsJob *job);
+static gboolean try (GVfsJob *job);
+static DBusMessage *create_reply (GVfsJob *job,
+ DBusConnection *connection,
+ DBusMessage *message);
+
+static void
+g_vfs_job_start_mountable_finalize (GObject *object)
+{
+ GVfsJobStartMountable *job;
+
+ job = G_VFS_JOB_START_MOUNTABLE (object);
+
+ if (job->mount_source)
+ g_object_unref (job->mount_source);
+
+ g_free (job->filename);
+
+ if (G_OBJECT_CLASS (g_vfs_job_start_mountable_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_vfs_job_start_mountable_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_job_start_mountable_class_init (GVfsJobStartMountableClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass);
+ GVfsJobDBusClass *job_dbus_class = G_VFS_JOB_DBUS_CLASS (klass);
+
+ gobject_class->finalize = g_vfs_job_start_mountable_finalize;
+ job_class->run = run;
+ job_class->try = try;
+ job_dbus_class->create_reply = create_reply;
+}
+
+static void
+g_vfs_job_start_mountable_init (GVfsJobStartMountable *job)
+{
+}
+
+GVfsJob *
+g_vfs_job_start_mountable_new (DBusConnection *connection,
+ DBusMessage *message,
+ GVfsBackend *backend)
+{
+ GVfsJobStartMountable *job;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError derror;
+ char *path;
+ const char *dbus_id, *obj_path;
+
+ dbus_error_init (&derror);
+ dbus_message_iter_init (message, &iter);
+
+ path = NULL;
+ if (!_g_dbus_message_iter_get_args (&iter, &derror,
+ G_DBUS_TYPE_CSTRING, &path,
+ DBUS_TYPE_STRING, &dbus_id,
+ DBUS_TYPE_OBJECT_PATH, &obj_path,
+ 0))
+ {
+ g_free (path);
+ reply = dbus_message_new_error (message,
+ derror.name,
+ derror.message);
+ dbus_error_free (&derror);
+
+ dbus_connection_send (connection, reply, NULL);
+ return NULL;
+ }
+
+ job = g_object_new (G_VFS_TYPE_JOB_START_MOUNTABLE,
+ "message", message,
+ "connection", connection,
+ NULL);
+
+ job->filename = path;
+ job->backend = backend;
+ job->mount_source = g_mount_source_new (dbus_id, obj_path);
+
+ return G_VFS_JOB (job);
+}
+
+static void
+run (GVfsJob *job)
+{
+ GVfsJobStartMountable *op_job = G_VFS_JOB_START_MOUNTABLE (job);
+ GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+ if (class->start_mountable == NULL)
+ {
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation not supported by backend"));
+ return;
+ }
+
+ class->start_mountable (op_job->backend,
+ op_job,
+ op_job->filename,
+ op_job->mount_source);
+}
+
+static gboolean
+try (GVfsJob *job)
+{
+ GVfsJobStartMountable *op_job = G_VFS_JOB_START_MOUNTABLE (job);
+ GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+ if (class->try_start_mountable == NULL)
+ return FALSE;
+
+ return class->try_start_mountable (op_job->backend,
+ op_job,
+ op_job->filename,
+ op_job->mount_source);
+}
+
+/* Might be called on an i/o thread */
+static DBusMessage *
+create_reply (GVfsJob *job,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ return reply;
+}
diff --git a/daemon/gvfsjobstartmountable.h b/daemon/gvfsjobstartmountable.h
new file mode 100644
index 00000000..95167f73
--- /dev/null
+++ b/daemon/gvfsjobstartmountable.h
@@ -0,0 +1,64 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VFS_JOB_START_MOUNTABLE_H__
+#define __G_VFS_JOB_START_MOUNTABLE_H__
+
+#include <gio/gio.h>
+#include <gvfsjob.h>
+#include <gvfsjobdbus.h>
+#include <gvfsbackend.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_JOB_START_MOUNTABLE (g_vfs_job_start_mountable_get_type ())
+#define G_VFS_JOB_START_MOUNTABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_START_MOUNTABLE, GVfsJobStartMountable))
+#define G_VFS_JOB_START_MOUNTABLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_START_MOUNTABLE, GVfsJobStartMountableClass))
+#define G_VFS_IS_JOB_START_MOUNTABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_START_MOUNTABLE))
+#define G_VFS_IS_JOB_START_MOUNTABLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_START_MOUNTABLE))
+#define G_VFS_JOB_START_MOUNTABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_START_MOUNTABLE, GVfsJobStartMountableClass))
+
+typedef struct _GVfsJobStartMountableClass GVfsJobStartMountableClass;
+
+struct _GVfsJobStartMountable
+{
+ GVfsJobDBus parent_instance;
+
+ GVfsBackend *backend;
+ char *filename;
+ GMountSource *mount_source;
+};
+
+struct _GVfsJobStartMountableClass
+{
+ GVfsJobDBusClass parent_class;
+};
+
+GType g_vfs_job_start_mountable_get_type (void) G_GNUC_CONST;
+
+GVfsJob *g_vfs_job_start_mountable_new (DBusConnection *connection,
+ DBusMessage *message,
+ GVfsBackend *backend);
+
+G_END_DECLS
+
+#endif /* __G_VFS_JOB_START_MOUNTABLE_H__ */
diff --git a/daemon/gvfsjobstopmountable.c b/daemon/gvfsjobstopmountable.c
new file mode 100644
index 00000000..63df2afc
--- /dev/null
+++ b/daemon/gvfsjobstopmountable.c
@@ -0,0 +1,164 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <glib/gi18n.h>
+#include "gvfsjobstopmountable.h"
+#include "gdbusutils.h"
+#include "gvfsdaemonutils.h"
+
+G_DEFINE_TYPE (GVfsJobStopMountable, g_vfs_job_stop_mountable, G_VFS_TYPE_JOB_DBUS)
+
+static void run (GVfsJob *job);
+static gboolean try (GVfsJob *job);
+static DBusMessage *create_reply (GVfsJob *job,
+ DBusConnection *connection,
+ DBusMessage *message);
+
+static void
+g_vfs_job_stop_mountable_finalize (GObject *object)
+{
+ GVfsJobStopMountable *job;
+
+ job = G_VFS_JOB_STOP_MOUNTABLE (object);
+
+ g_free (job->filename);
+
+ if (G_OBJECT_CLASS (g_vfs_job_stop_mountable_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_vfs_job_stop_mountable_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_job_stop_mountable_class_init (GVfsJobStopMountableClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass);
+ GVfsJobDBusClass *job_dbus_class = G_VFS_JOB_DBUS_CLASS (klass);
+
+ gobject_class->finalize = g_vfs_job_stop_mountable_finalize;
+ job_class->run = run;
+ job_class->try = try;
+ job_dbus_class->create_reply = create_reply;
+}
+
+static void
+g_vfs_job_stop_mountable_init (GVfsJobStopMountable *job)
+{
+}
+
+GVfsJob *
+g_vfs_job_stop_mountable_new (DBusConnection *connection,
+ DBusMessage *message,
+ GVfsBackend *backend)
+{
+ GVfsJobStopMountable *job;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError derror;
+ char *path;
+ guint32 flags;
+
+ dbus_error_init (&derror);
+ dbus_message_iter_init (message, &iter);
+
+ path = NULL;
+ if (!_g_dbus_message_iter_get_args (&iter, &derror,
+ G_DBUS_TYPE_CSTRING, &path,
+ DBUS_TYPE_UINT32, &flags,
+ 0))
+ {
+ g_free (path);
+ reply = dbus_message_new_error (message,
+ derror.name,
+ derror.message);
+ dbus_error_free (&derror);
+
+ dbus_connection_send (connection, reply, NULL);
+ return NULL;
+ }
+
+ job = g_object_new (G_VFS_TYPE_JOB_STOP_MOUNTABLE,
+ "message", message,
+ "connection", connection,
+ NULL);
+
+ job->filename = path;
+ job->backend = backend;
+ job->flags = flags;
+
+ return G_VFS_JOB (job);
+}
+
+static void
+run (GVfsJob *job)
+{
+ GVfsJobStopMountable *op_job = G_VFS_JOB_STOP_MOUNTABLE (job);
+ GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+ if (class->stop_mountable == NULL)
+ {
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation not supported by backend"));
+ return;
+ }
+
+ class->stop_mountable (op_job->backend,
+ op_job,
+ op_job->filename,
+ op_job->flags);
+}
+
+static gboolean
+try (GVfsJob *job)
+{
+ GVfsJobStopMountable *op_job = G_VFS_JOB_STOP_MOUNTABLE (job);
+ GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+ if (class->try_stop_mountable == NULL)
+ return FALSE;
+
+ return class->try_stop_mountable (op_job->backend,
+ op_job,
+ op_job->filename,
+ op_job->flags);
+}
+
+/* Might be called on an i/o thread */
+static DBusMessage *
+create_reply (GVfsJob *job,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ return reply;
+}
diff --git a/daemon/gvfsjobstopmountable.h b/daemon/gvfsjobstopmountable.h
new file mode 100644
index 00000000..e584ce53
--- /dev/null
+++ b/daemon/gvfsjobstopmountable.h
@@ -0,0 +1,64 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VFS_JOB_STOP_MOUNTABLE_H__
+#define __G_VFS_JOB_STOP_MOUNTABLE_H__
+
+#include <gio/gio.h>
+#include <gvfsjob.h>
+#include <gvfsjobdbus.h>
+#include <gvfsbackend.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_JOB_STOP_MOUNTABLE (g_vfs_job_stop_mountable_get_type ())
+#define G_VFS_JOB_STOP_MOUNTABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_STOP_MOUNTABLE, GVfsJobStopMountable))
+#define G_VFS_JOB_STOP_MOUNTABLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_STOP_MOUNTABLE, GVfsJobStopMountableClass))
+#define G_VFS_IS_JOB_STOP_MOUNTABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_STOP_MOUNTABLE))
+#define G_VFS_IS_JOB_STOP_MOUNTABLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_STOP_MOUNTABLE))
+#define G_VFS_JOB_STOP_MOUNTABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_STOP_MOUNTABLE, GVfsJobStopMountableClass))
+
+typedef struct _GVfsJobStopMountableClass GVfsJobStopMountableClass;
+
+struct _GVfsJobStopMountable
+{
+ GVfsJobDBus parent_instance;
+
+ GVfsBackend *backend;
+ char *filename;
+ GMountUnmountFlags flags;
+};
+
+struct _GVfsJobStopMountableClass
+{
+ GVfsJobDBusClass parent_class;
+};
+
+GType g_vfs_job_stop_mountable_get_type (void) G_GNUC_CONST;
+
+GVfsJob *g_vfs_job_stop_mountable_new (DBusConnection *connection,
+ DBusMessage *message,
+ GVfsBackend *backend);
+
+G_END_DECLS
+
+#endif /* __G_VFS_JOB_STOP_MOUNTABLE_H__ */
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);
diff --git a/programs/gvfs-mount.c b/programs/gvfs-mount.c
index b72b31d2..b2699a66 100644
--- a/programs/gvfs-mount.c
+++ b/programs/gvfs-mount.c
@@ -542,6 +542,9 @@ list_drives (GList *drives,
if (extra_detail)
{
+ GEnumValue *enum_value;
+ gpointer klass;
+
ids = g_drive_enumerate_identifiers (drive);
if (ids && ids[0] != NULL)
{
@@ -569,8 +572,19 @@ list_drives (GList *drives,
g_print ("%*sis_media_check_automatic=%d\n", indent + 2, "", g_drive_is_media_check_automatic (drive));
g_print ("%*scan_poll_for_media=%d\n", indent + 2, "", g_drive_can_poll_for_media (drive));
g_print ("%*scan_eject=%d\n", indent + 2, "", g_drive_can_eject (drive));
+ g_print ("%*scan_start=%d\n", indent + 2, "", g_drive_can_start (drive));
+ g_print ("%*scan_stop=%d\n", indent + 2, "", g_drive_can_stop (drive));
+
+ enum_value = NULL;
+ klass = g_type_class_ref (G_TYPE_DRIVE_START_STOP_TYPE);
+ if (klass != NULL)
+ {
+ enum_value = g_enum_get_value (klass, g_drive_get_start_stop_type (drive));
+ g_print ("%*sstart_stop_type=%s\n", indent + 2, "",
+ enum_value != NULL ? enum_value->value_nick : "UNKNOWN");
+ g_type_class_unref (klass);
+ }
}
-
volumes = g_drive_get_volumes (drive);
list_volumes (volumes, indent + 2, FALSE);
g_list_foreach (volumes, (GFunc)g_object_unref, NULL);