[gvfs/wip/udisks2] Spawn helpers asynchronously, not synchronously
- From: David Zeuthen <davidz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gvfs/wip/udisks2] Spawn helpers asynchronously, not synchronously
- Date: Thu, 22 Dec 2011 19:25:33 +0000 (UTC)
commit b9ccc6617f230a96938642ecb0aa65da3530e5d8
Author: David Zeuthen <davidz redhat com>
Date: Thu Dec 22 14:24:46 2011 -0500
Spawn helpers asynchronously, not synchronously
Signed-off-by: David Zeuthen <davidz redhat com>
monitor/udisks2/gvfsudisks2mount.c | 343 +++++++++++++++++----------------
monitor/udisks2/gvfsudisks2utils.c | 358 +++++++++++++++++++++++++++++++++++
monitor/udisks2/gvfsudisks2utils.h | 13 ++
monitor/udisks2/gvfsudisks2volume.c | 47 +++---
4 files changed, 572 insertions(+), 189 deletions(-)
---
diff --git a/monitor/udisks2/gvfsudisks2mount.c b/monitor/udisks2/gvfsudisks2mount.c
index a38220d..ca1a075 100644
--- a/monitor/udisks2/gvfsudisks2mount.c
+++ b/monitor/udisks2/gvfsudisks2mount.c
@@ -511,76 +511,11 @@ gvfs_udisks2_mount_can_eject (GMount *_mount)
/* ---------------------------------------------------------------------------------------------------- */
-static GArray *
-get_busy_processes (const gchar* const *mount_points)
-{
- GArray *processes;
- guint n;
-
- processes = g_array_new (FALSE, FALSE, sizeof (GPid));
-
- for (n = 0; mount_points != NULL && mount_points[n] != NULL; n++)
- {
- const gchar *mount_point = mount_points[n];
- const gchar *lsof_argv[] = {"lsof", "-t", NULL, NULL};
- gchar *standard_output = NULL;
- const gchar *p;
- gint exit_status;
- GError *error;
-
- lsof_argv[2] = mount_point;
-
- error = NULL;
- if (!g_spawn_sync (NULL, /* working_directory */
- (gchar **) lsof_argv,
- NULL, /* envp */
- G_SPAWN_SEARCH_PATH,
- NULL, /* child_setup */
- NULL, /* user_data */
- &standard_output,
- NULL, /* standard_error */
- &exit_status,
- &error))
- {
- g_warning ("Error launching lsof(1): %s (%s, %d)",
- error->message, g_quark_to_string (error->domain), error->code);
- goto cont_loop;
- }
- if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0))
- {
- g_warning ("lsof(1) didn't exit normally");
- goto cont_loop;
- }
-
- p = standard_output;
- while (TRUE)
- {
- GPid pid;
- gchar *endp;
-
- if (*p == '\0')
- break;
-
- pid = strtol (p, &endp, 10);
-
- if (pid == 0 && p == endp)
- break;
-
- g_array_append_val (processes, pid);
-
- p = endp;
- }
- cont_loop:
- g_free (standard_output);
- }
- return processes;
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
typedef struct
{
+ volatile gint ref_count;
GSimpleAsyncResult *simple;
+ gboolean completed;
GVfsUDisks2Mount *mount;
@@ -588,43 +523,47 @@ typedef struct
UDisksFilesystem *filesystem;
GCancellable *cancellable;
- gulong cancelled_handler_id;
- gboolean is_cancelled;
GMountOperation *mount_operation;
GMountUnmountFlags flags;
gulong mount_op_reply_handler_id;
guint retry_unmount_timer_id;
-
} UnmountData;
-static void
-unmount_data_free (UnmountData *data)
+static UnmountData *
+unmount_data_ref (UnmountData *data)
{
- g_object_unref (data->simple);
+ g_atomic_int_inc (&data->ref_count);
+ return data;
+}
- if (data->mount_op_reply_handler_id > 0)
- {
- /* make the operation dialog go away */
- g_signal_emit_by_name (data->mount_operation, "aborted");
- g_signal_handler_disconnect (data->mount_operation, data->mount_op_reply_handler_id);
- }
- if (data->retry_unmount_timer_id > 0)
+static void
+unmount_data_unref (UnmountData *data)
+{
+ if (g_atomic_int_dec_and_test (&data->ref_count))
{
- g_source_remove (data->retry_unmount_timer_id);
- data->retry_unmount_timer_id = 0;
- }
+ g_object_unref (data->simple);
- g_clear_object (&data->mount);
- if (data->cancelled_handler_id != 0)
- g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
- g_clear_object (&data->cancellable);
- g_clear_object (&data->mount_operation);
- g_clear_object (&data->encrypted);
- g_clear_object (&data->filesystem);
+ if (data->mount_op_reply_handler_id > 0)
+ {
+ /* make the operation dialog go away */
+ g_signal_emit_by_name (data->mount_operation, "aborted");
+ g_signal_handler_disconnect (data->mount_operation, data->mount_op_reply_handler_id);
+ }
+ if (data->retry_unmount_timer_id > 0)
+ {
+ g_source_remove (data->retry_unmount_timer_id);
+ data->retry_unmount_timer_id = 0;
+ }
- g_free (data);
+ g_clear_object (&data->mount);
+ g_clear_object (&data->cancellable);
+ g_clear_object (&data->mount_operation);
+ g_clear_object (&data->encrypted);
+ g_clear_object (&data->filesystem);
+ g_free (data);
+ }
}
static void unmount_do (UnmountData *data, gboolean force);
@@ -673,7 +612,8 @@ on_mount_op_reply (GMountOperation *mount_operation,
"GMountOperation aborted (user should never see this "
"error since it is G_IO_ERROR_FAILED_HANDLED)");
g_simple_async_result_complete_in_idle (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
}
else if (result == G_MOUNT_OPERATION_HANDLED)
{
@@ -690,46 +630,118 @@ on_mount_op_reply (GMountOperation *mount_operation,
G_IO_ERROR_BUSY,
_("One or more programs are preventing the unmount operation."));
g_simple_async_result_complete_in_idle (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
}
}
static void
-unmount_show_busy (UnmountData *data,
- const gchar *const *mount_points)
+lsof_command_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
+ UnmountData *data = user_data;
+ GError *error;
+ gint exit_status;
GArray *processes;
const gchar *choices[3] = {NULL, NULL, NULL};
const gchar *message;
+ gchar *standard_output = NULL;
+ const gchar *p;
- processes = get_busy_processes (mount_points);
+ processes = g_array_new (FALSE, FALSE, sizeof (GPid));
- if (data->mount_op_reply_handler_id == 0)
+ error = NULL;
+ if (!gvfs_udisks2_utils_spawn_finish (res,
+ &exit_status,
+ &standard_output,
+ NULL, /* gchar **out_standard_error */
+ &error))
{
- data->mount_op_reply_handler_id = g_signal_connect (data->mount_operation,
- "reply",
- G_CALLBACK (on_mount_op_reply),
- data);
+ g_printerr ("Error launching lsof(1): %s (%s, %d)\n",
+ error->message, g_quark_to_string (error->domain), error->code);
+ g_error_free (error);
+ goto out;
}
- choices[0] = _("Unmount Anyway");
- choices[1] = _("Cancel");
- message = _("Volume is busy\n"
- "One or more applications are keeping the volume busy.");
- g_signal_emit_by_name (data->mount_operation,
- "show-processes",
- message,
- processes,
- choices);
- /* set up a timer to try unmounting every two seconds - this will also
- * update the list of busy processes
- */
- if (data->retry_unmount_timer_id == 0)
+
+ if (!(WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0))
+ {
+ g_printerr ("lsof(1) did not exit normally\n");
+ goto out;
+ }
+
+ p = standard_output;
+ while (TRUE)
{
- data->retry_unmount_timer_id = g_timeout_add_seconds (2,
- on_retry_timer_cb,
- data);
+ GPid pid;
+ gchar *endp;
+
+ if (*p == '\0')
+ break;
+
+ pid = strtol (p, &endp, 10);
+ if (pid == 0 && p == endp)
+ break;
+
+ g_array_append_val (processes, pid);
+
+ p = endp;
}
- g_array_free (processes, TRUE);
+
+ out:
+ if (!data->completed)
+ {
+ /* We want to emit the 'show-processes' signal even if launching
+ * lsof(1) failed or if it didn't return any PIDs. This is because
+ * it won't show e.g. root-owned processes operating on files
+ * on the mount point.
+ *
+ * (unfortunately there's no way to convey that it failed)
+ */
+ if (data->mount_op_reply_handler_id == 0)
+ {
+ data->mount_op_reply_handler_id = g_signal_connect (data->mount_operation,
+ "reply",
+ G_CALLBACK (on_mount_op_reply),
+ data);
+ }
+ choices[0] = _("Unmount Anyway");
+ choices[1] = _("Cancel");
+ message = _("Volume is busy\n"
+ "One or more applications are keeping the volume busy.");
+ g_signal_emit_by_name (data->mount_operation,
+ "show-processes",
+ message,
+ processes,
+ choices);
+ /* set up a timer to try unmounting every two seconds - this will also
+ * update the list of busy processes
+ */
+ if (data->retry_unmount_timer_id == 0)
+ {
+ data->retry_unmount_timer_id = g_timeout_add_seconds (2,
+ on_retry_timer_cb,
+ data);
+ }
+ g_array_free (processes, TRUE);
+ g_free (standard_output);
+ }
+ unmount_data_unref (data); /* return ref */
+}
+
+
+static void
+unmount_show_busy (UnmountData *data,
+ const gchar *mount_point)
+{
+ gchar *escaped_mount_point;
+ escaped_mount_point = g_strescape (mount_point, NULL);
+ gvfs_udisks2_utils_spawn (data->cancellable,
+ lsof_command_cb,
+ unmount_data_ref (data),
+ "lsof -t \"%s\"",
+ escaped_mount_point);
+ g_free (escaped_mount_point);
}
static void
@@ -747,7 +759,8 @@ lock_cb (GObject *source_object,
&error))
g_simple_async_result_take_error (data->simple, error);
g_simple_async_result_complete (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
}
static void
@@ -769,14 +782,10 @@ unmount_cb (GObject *source_object,
/* if the user passed in a GMountOperation, then do the GMountOperation::show-processes dance ... */
if (error->code == G_IO_ERROR_BUSY && data->mount_operation != NULL)
{
- unmount_show_busy (data, udisks_filesystem_get_mount_points (filesystem));
+ unmount_show_busy (data, udisks_filesystem_get_mount_points (filesystem)[0]);
goto out;
}
- else
- {
- g_simple_async_result_take_error (data->simple, error);
- g_simple_async_result_complete (data->simple);
- }
+ g_simple_async_result_take_error (data->simple, error);
}
else
{
@@ -790,53 +799,40 @@ unmount_cb (GObject *source_object,
data);
goto out;
}
- else
- {
- g_simple_async_result_complete (data->simple);
- }
}
- unmount_data_free (data);
+ g_simple_async_result_complete (data->simple);
+ data->completed = TRUE;
+ unmount_data_unref (data);
out:
;
}
+
+/* ------------------------------ */
+
static void
-unmount_do_command (UnmountData *data,
- gboolean force)
+umount_command_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
+ UnmountData *data = user_data;
GError *error;
gint exit_status;
gchar *standard_error = NULL;
- const gchar *umount_argv[4] = {"umount", NULL, NULL, NULL};
- if (force)
- {
- umount_argv[1] = "-l";
- umount_argv[2] = data->mount->mount_path;
- }
- else
- {
- umount_argv[1] = data->mount->mount_path;
- }
-
- /* TODO: we could do this async but it's probably not worth the effort */
error = NULL;
- if (!g_spawn_sync (NULL, /* working dir */
- (gchar **) umount_argv,
- NULL, /* envp */
- G_SPAWN_SEARCH_PATH,
- NULL, /* child_setup */
- NULL, /* user_data for child_setup */
- NULL, /* standard_output */
- &standard_error, /* standard_error */
- &exit_status,
- &error))
+ if (!gvfs_udisks2_utils_spawn_finish (res,
+ &exit_status,
+ NULL, /* gchar **out_standard_output */
+ &standard_error,
+ &error))
{
- g_prefix_error (&error, "Error running umount: ");
+ g_simple_async_result_take_error (data->simple, error);
g_simple_async_result_take_error (data->simple, error);
g_simple_async_result_complete (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
goto out;
}
@@ -844,15 +840,14 @@ unmount_do_command (UnmountData *data,
{
gvfs_udisks2_volume_monitor_update (data->mount->monitor);
g_simple_async_result_complete (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
goto out;
}
if (standard_error != NULL && strstr (standard_error, "device is busy") != NULL)
{
- const gchar *mount_points[2] = {NULL, NULL};
- mount_points[0] = data->mount->mount_path;
- unmount_show_busy (data, mount_points);
+ unmount_show_busy (data, data->mount->mount_path);
goto out;
}
@@ -861,7 +856,8 @@ unmount_do_command (UnmountData *data,
G_IO_ERROR_FAILED,
standard_error);
g_simple_async_result_complete (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
out:
g_free (standard_error);
@@ -873,9 +869,18 @@ unmount_do (UnmountData *data,
{
GVariantBuilder builder;
+ /* Use the umount(8) command if there is no block device / filesystem */
if (data->filesystem == NULL)
{
- unmount_do_command (data, force);
+ gchar *escaped_mount_path;
+ escaped_mount_path = g_strescape (data->mount->mount_path, NULL);
+ gvfs_udisks2_utils_spawn (data->cancellable,
+ umount_command_cb,
+ data,
+ "umount %s \"%s\"",
+ force ? "-l " : "",
+ escaped_mount_path);
+ g_free (escaped_mount_path);
goto out;
}
@@ -918,6 +923,7 @@ gvfs_udisks2_mount_unmount_with_operation (GMount *_mount,
g_signal_emit_by_name (mount->monitor, "mount-pre-unmount", mount);
data = g_new0 (UnmountData, 1);
+ data->ref_count = 1;
data->simple = g_simple_async_result_new (G_OBJECT (mount),
callback,
user_data,
@@ -931,7 +937,8 @@ gvfs_udisks2_mount_unmount_with_operation (GMount *_mount,
{
/* burn mounts are really never mounted so complete successfully immediately */
g_simple_async_result_complete_in_idle (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
goto out;
}
@@ -949,7 +956,9 @@ gvfs_udisks2_mount_unmount_with_operation (GMount *_mount,
G_IO_ERROR_FAILED,
"No object for D-Bus interface");
g_simple_async_result_complete (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
+ goto out;
}
data->filesystem = udisks_object_get_filesystem (UDISKS_OBJECT (object));
if (data->filesystem == NULL)
@@ -964,7 +973,8 @@ gvfs_udisks2_mount_unmount_with_operation (GMount *_mount,
G_IO_ERROR_FAILED,
"No filesystem or encrypted interface on D-Bus object");
g_simple_async_result_complete (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
goto out;
}
@@ -981,7 +991,8 @@ gvfs_udisks2_mount_unmount_with_operation (GMount *_mount,
G_IO_ERROR_FAILED,
"No filesystem interface on D-Bus object for cleartext device");
g_simple_async_result_complete (data->simple);
- unmount_data_free (data);
+ data->completed = TRUE;
+ unmount_data_unref (data);
goto out;
}
}
diff --git a/monitor/udisks2/gvfsudisks2utils.c b/monitor/udisks2/gvfsudisks2utils.c
index 1906a00..213c2e7 100644
--- a/monitor/udisks2/gvfsudisks2utils.c
+++ b/monitor/udisks2/gvfsudisks2utils.c
@@ -105,3 +105,361 @@ gvfs_udisks2_utils_lookup_fstab_options_value (const gchar *fstab_options,
}
return ret;
}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GSimpleAsyncResult *simple; /* borrowed reference */
+ GMainContext *main_context; /* may be NULL */
+
+ gchar *command_line;
+
+ GCancellable *cancellable; /* may be NULL */
+ gulong cancellable_handler_id;
+
+ GPid child_pid;
+ gint child_stdout_fd;
+ gint child_stderr_fd;
+
+ GIOChannel *child_stdout_channel;
+ GIOChannel *child_stderr_channel;
+
+ GSource *child_watch_source;
+ GSource *child_stdout_source;
+ GSource *child_stderr_source;
+
+ GString *child_stdout;
+ GString *child_stderr;
+
+ gint exit_status;
+} SpawnData;
+
+static void
+child_watch_from_release_cb (GPid pid,
+ gint status,
+ gpointer user_data)
+{
+}
+
+static void
+spawn_data_free (SpawnData *data)
+{
+ /* Nuke the child, if necessary */
+ if (data->child_watch_source != NULL)
+ {
+ g_source_destroy (data->child_watch_source);
+ data->child_watch_source = NULL;
+ }
+
+ if (data->child_pid != 0)
+ {
+ GSource *source;
+ kill (data->child_pid, SIGTERM);
+ /* OK, we need to reap for the child ourselves - we don't want
+ * to use waitpid() because that might block the calling
+ * thread (the child might handle SIGTERM and use several
+ * seconds for cleanup/rollback).
+ *
+ * So we use GChildWatch instead.
+ *
+ * Avoid taking a references to ourselves. but note that we need
+ * to pass the GSource so we can nuke it once handled.
+ */
+ source = g_child_watch_source_new (data->child_pid);
+ g_source_set_callback (source,
+ (GSourceFunc) child_watch_from_release_cb,
+ source,
+ (GDestroyNotify) g_source_destroy);
+ g_source_attach (source, data->main_context);
+ g_source_unref (source);
+ data->child_pid = 0;
+ }
+
+ if (data->child_stdout != NULL)
+ {
+ g_string_free (data->child_stdout, TRUE);
+ data->child_stdout = NULL;
+ }
+
+ if (data->child_stderr != NULL)
+ {
+ g_string_free (data->child_stderr, TRUE);
+ data->child_stderr = NULL;
+ }
+
+ if (data->child_stdout_channel != NULL)
+ {
+ g_io_channel_unref (data->child_stdout_channel);
+ data->child_stdout_channel = NULL;
+ }
+ if (data->child_stderr_channel != NULL)
+ {
+ g_io_channel_unref (data->child_stderr_channel);
+ data->child_stderr_channel = NULL;
+ }
+
+ if (data->child_stdout_source != NULL)
+ {
+ g_source_destroy (data->child_stdout_source);
+ data->child_stdout_source = NULL;
+ }
+ if (data->child_stderr_source != NULL)
+ {
+ g_source_destroy (data->child_stderr_source);
+ data->child_stderr_source = NULL;
+ }
+
+ if (data->child_stdout_fd != -1)
+ {
+ g_warn_if_fail (close (data->child_stdout_fd) == 0);
+ data->child_stdout_fd = -1;
+ }
+ if (data->child_stderr_fd != -1)
+ {
+ g_warn_if_fail (close (data->child_stderr_fd) == 0);
+ data->child_stderr_fd = -1;
+ }
+
+ if (data->cancellable_handler_id > 0)
+ {
+ g_cancellable_disconnect (data->cancellable, data->cancellable_handler_id);
+ data->cancellable_handler_id = 0;
+ }
+
+ if (data->main_context != NULL)
+ g_main_context_unref (data->main_context);
+
+ if (data->cancellable != NULL)
+ g_object_unref (data->cancellable);
+
+ g_free (data->command_line);
+
+ g_slice_free (SpawnData, data);
+}
+
+/* called in the thread where @cancellable was cancelled */
+static void
+on_cancelled (GCancellable *cancellable,
+ gpointer user_data)
+{
+ SpawnData *data = user_data;
+ GError *error;
+
+ error = NULL;
+ g_warn_if_fail (g_cancellable_set_error_if_cancelled (cancellable, &error));
+ g_simple_async_result_take_error (data->simple, error);
+ g_simple_async_result_complete_in_idle (data->simple);
+ g_object_unref (data->simple);
+}
+
+static gboolean
+read_child_stderr (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ SpawnData *data = user_data;
+ gchar buf[1024];
+ gsize bytes_read;
+
+ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+ g_string_append_len (data->child_stderr, buf, bytes_read);
+ return TRUE;
+}
+
+static gboolean
+read_child_stdout (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ SpawnData *data = user_data;
+ gchar buf[1024];
+ gsize bytes_read;
+
+ g_io_channel_read_chars (channel, buf, sizeof buf, &bytes_read, NULL);
+ g_string_append_len (data->child_stdout, buf, bytes_read);
+ return TRUE;
+}
+
+static void
+child_watch_cb (GPid pid,
+ gint status,
+ gpointer user_data)
+{
+ SpawnData *data = user_data;
+ gchar *buf;
+ gsize buf_size;
+
+ if (g_io_channel_read_to_end (data->child_stdout_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+ {
+ g_string_append_len (data->child_stdout, buf, buf_size);
+ g_free (buf);
+ }
+ if (g_io_channel_read_to_end (data->child_stderr_channel, &buf, &buf_size, NULL) == G_IO_STATUS_NORMAL)
+ {
+ g_string_append_len (data->child_stderr, buf, buf_size);
+ g_free (buf);
+ }
+
+ data->exit_status = status;
+
+ /* ok, child watch is history, make sure we don't free it in spawn_data_free() */
+ data->child_pid = 0;
+ data->child_watch_source = NULL;
+
+ /* we're done */
+ g_simple_async_result_complete_in_idle (data->simple);
+ g_object_unref (data->simple);
+}
+
+void
+gvfs_udisks2_utils_spawn (GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ const gchar *command_line_format,
+ ...)
+{
+ va_list var_args;
+ SpawnData *data;
+ GError *error;
+ gint child_argc;
+ gchar **child_argv = NULL;
+
+ data = g_slice_new0 (SpawnData);
+ data->simple = g_simple_async_result_new (NULL,
+ callback,
+ user_data,
+ gvfs_udisks2_utils_spawn);
+ data->main_context = g_main_context_get_thread_default ();
+ if (data->main_context != NULL)
+ g_main_context_ref (data->main_context);
+
+ data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL;
+
+ va_start (var_args, command_line_format);
+ data->command_line = g_strdup_vprintf (command_line_format, var_args);
+ va_end (var_args);
+
+ data->child_stdout = g_string_new (NULL);
+ data->child_stderr = g_string_new (NULL);
+ data->child_stdout_fd = -1;
+ data->child_stderr_fd = -1;
+
+ /* the life-cycle of SpawnData is tied to its GSimpleAsyncResult */
+ g_simple_async_result_set_op_res_gpointer (data->simple, data, (GDestroyNotify) spawn_data_free);
+
+ error = NULL;
+ if (data->cancellable != NULL)
+ {
+ /* could already be cancelled */
+ error = NULL;
+ if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+ {
+ g_simple_async_result_take_error (data->simple, error);
+ g_simple_async_result_complete_in_idle (data->simple);
+ g_object_unref (data->simple);
+ goto out;
+ }
+
+ data->cancellable_handler_id = g_cancellable_connect (data->cancellable,
+ G_CALLBACK (on_cancelled),
+ data,
+ NULL);
+ }
+
+ error = NULL;
+ if (!g_shell_parse_argv (data->command_line,
+ &child_argc,
+ &child_argv,
+ &error))
+ {
+ g_prefix_error (&error,
+ "Error parsing command-line `%s': ",
+ data->command_line);
+ g_simple_async_result_take_error (data->simple, error);
+ g_simple_async_result_complete_in_idle (data->simple);
+ g_object_unref (data->simple);
+ goto out;
+ }
+
+ error = NULL;
+ if (!g_spawn_async_with_pipes (NULL, /* working directory */
+ child_argv,
+ NULL, /* envp */
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, /* child_setup */
+ NULL, /* child_setup's user_data */
+ &(data->child_pid),
+ NULL, /* gint *stdin_fd */
+ &(data->child_stdout_fd),
+ &(data->child_stderr_fd),
+ &error))
+ {
+ g_prefix_error (&error,
+ "Error spawning command-line `%s': ",
+ data->command_line);
+ g_simple_async_result_take_error (data->simple, error);
+ g_simple_async_result_complete_in_idle (data->simple);
+ g_object_unref (data->simple);
+ goto out;
+ }
+
+ data->child_watch_source = g_child_watch_source_new (data->child_pid);
+ g_source_set_callback (data->child_watch_source, (GSourceFunc) child_watch_cb, data, NULL);
+ g_source_attach (data->child_watch_source, data->main_context);
+ g_source_unref (data->child_watch_source);
+
+ data->child_stdout_channel = g_io_channel_unix_new (data->child_stdout_fd);
+ g_io_channel_set_flags (data->child_stdout_channel, G_IO_FLAG_NONBLOCK, NULL);
+ data->child_stdout_source = g_io_create_watch (data->child_stdout_channel, G_IO_IN);
+ g_source_set_callback (data->child_stdout_source, (GSourceFunc) read_child_stdout, data, NULL);
+ g_source_attach (data->child_stdout_source, data->main_context);
+ g_source_unref (data->child_stdout_source);
+
+ data->child_stderr_channel = g_io_channel_unix_new (data->child_stderr_fd);
+ g_io_channel_set_flags (data->child_stderr_channel, G_IO_FLAG_NONBLOCK, NULL);
+ data->child_stderr_source = g_io_create_watch (data->child_stderr_channel, G_IO_IN);
+ g_source_set_callback (data->child_stderr_source, (GSourceFunc) read_child_stderr, data, NULL);
+ g_source_attach (data->child_stderr_source, data->main_context);
+ g_source_unref (data->child_stderr_source);
+
+ out:
+ g_strfreev (child_argv);
+}
+
+gboolean
+gvfs_udisks2_utils_spawn_finish (GAsyncResult *res,
+ gint *out_exit_status,
+ gchar **out_standard_output,
+ gchar **out_standard_error,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ SpawnData *data;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == gvfs_udisks2_utils_spawn);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ goto out;
+
+ data = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (out_exit_status != NULL)
+ *out_exit_status = data->exit_status;
+
+ if (out_standard_output != NULL)
+ *out_standard_output = g_strdup (data->child_stdout->str);
+
+ if (out_standard_error != NULL)
+ *out_standard_error = g_strdup (data->child_stderr->str);
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
diff --git a/monitor/udisks2/gvfsudisks2utils.h b/monitor/udisks2/gvfsudisks2utils.h
index ad3d6d7..7a6abe7 100644
--- a/monitor/udisks2/gvfsudisks2utils.h
+++ b/monitor/udisks2/gvfsudisks2utils.h
@@ -37,6 +37,19 @@ GIcon *gvfs_udisks2_utils_icon_from_fs_type (const gchar *fs_type);
gchar *gvfs_udisks2_utils_lookup_fstab_options_value (const gchar *fstab_options,
const gchar *key);
+void gvfs_udisks2_utils_spawn (GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ const gchar *command_line_format,
+ ...);
+
+gboolean gvfs_udisks2_utils_spawn_finish (GAsyncResult *res,
+ gint *out_exit_status,
+ gchar **out_standard_output,
+ gchar **out_standard_error,
+ GError **error);
+
+
G_END_DECLS
#endif /* __GVFS_UDISKS2_UTILS_H__ */
diff --git a/monitor/udisks2/gvfsudisks2volume.c b/monitor/udisks2/gvfsudisks2volume.c
index 2415452..8914b02 100644
--- a/monitor/udisks2/gvfsudisks2volume.c
+++ b/monitor/udisks2/gvfsudisks2volume.c
@@ -743,42 +743,35 @@ mount_cancel_pending_op (MountData *data)
/* ------------------------------ */
static void
-do_mount_command (MountData *data)
+mount_command_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
+ MountData *data = user_data;
GError *error;
gint exit_status;
gchar *standard_error = NULL;
- const gchar *mount_argv[3] = {"mount", NULL, NULL};
- mount_argv[1] = g_unix_mount_point_get_mount_path (data->volume->mount_point);
+ /* TODO: for e.g. NFS and CIFS mounts we could do GMountOperation stuff and pipe a
+ * password to mount(8)'s stdin channel
+ *
+ * TODO: if this fails because the user is not authorized (e.g. EPERM), we could
+ * run it through a polkit-ified setuid root helper
+ */
- /* TODO: we could do this async but it's probably not worth the effort */
error = NULL;
- if (!g_spawn_sync (NULL, /* working dir */
- (gchar **) mount_argv,
- NULL, /* envp */
- G_SPAWN_SEARCH_PATH,
- NULL, /* child_setup */
- NULL, /* user_data for child_setup */
- NULL, /* standard_output */
- &standard_error, /* standard_error */
- &exit_status,
- &error))
+ if (!gvfs_udisks2_utils_spawn_finish (res,
+ &exit_status,
+ NULL, /* gchar **out_standard_output */
+ &standard_error,
+ &error))
{
- g_prefix_error (&error, "Error running mount: ");
g_simple_async_result_take_error (data->simple, error);
g_simple_async_result_complete (data->simple);
mount_data_free (data);
goto out;
}
- /* TODO: for e.g. NFS and CIFS mounts we could do GMountOperation stuff and pipe a
- * password to mount(8)'s stdin channel
- *
- * TODO: if this fails because the user is not authorized (e.g. EPERM), we could
- * run it through a polkit-ified setuid root helper
- */
-
if (WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0)
{
gvfs_udisks2_volume_monitor_update (data->volume->monitor);
@@ -1057,9 +1050,17 @@ gvfs_udisks2_volume_mount (GVolume *_volume,
}
volume->mount_pending_op = data;
+ /* Use the mount(8) command if there is no block device */
if (volume->block == NULL)
{
- do_mount_command (data);
+ gchar *escaped_mount_path;
+ escaped_mount_path = g_strescape (g_unix_mount_point_get_mount_path (data->volume->mount_point), NULL);
+ gvfs_udisks2_utils_spawn (data->cancellable,
+ mount_command_cb,
+ data,
+ "mount \"%s\"",
+ escaped_mount_path);
+ g_free (escaped_mount_path);
goto out;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]