Use g_simple_async_result_{new_,}take_error
[platform/upstream/glib.git] / gio / gunixmount.c
index 6246357..9da2474 100644 (file)
@@ -23,7 +23,7 @@
  *         David Zeuthen <davidz@redhat.com>
  */
 
-#include <config.h>
+#include "config.h"
 
 #include <string.h>
 #include <sys/wait.h>
 #include <glib.h>
 #include "gunixvolumemonitor.h"
 #include "gunixmount.h"
+#include "gunixmounts.h"
 #include "gunixvolume.h"
 #include "gmountprivate.h"
+#include "gmount.h"
+#include "gfile.h"
 #include "gvolumemonitor.h"
 #include "gthemedicon.h"
 #include "gsimpleasyncresult.h"
+#include "gioerror.h"
 #include "glibintl.h"
+/* for BUFSIZ */
+#include <stdio.h>
 
-#include "gioalias.h"
 
 struct _GUnixMount {
   GObject parent;
@@ -52,6 +57,8 @@ struct _GUnixMount {
   GIcon *icon;
   char *device_path;
   char *mount_path;
+
+  gboolean can_eject;
 };
 
 static void g_unix_mount_mount_iface_init (GMountIface *iface);
@@ -75,14 +82,13 @@ g_unix_mount_finalize (GObject *object)
   if (mount->volume)
     _g_unix_volume_unset_mount (mount->volume, mount);
     
-  //TODO: g_warn_if_fail (volume->volume == NULL);
+  /* TODO: g_warn_if_fail (volume->volume == NULL); */
   g_object_unref (mount->icon);
   g_free (mount->name);
   g_free (mount->device_path);
   g_free (mount->mount_path);
-  
-  if (G_OBJECT_CLASS (g_unix_mount_parent_class)->finalize)
-    (*G_OBJECT_CLASS (g_unix_mount_parent_class)->finalize) (object);
+
+  G_OBJECT_CLASS (g_unix_mount_parent_class)->finalize (object);
 }
 
 static void
@@ -99,20 +105,22 @@ g_unix_mount_init (GUnixMount *unix_mount)
 }
 
 GUnixMount *
-_g_unix_mount_new (GVolumeMonitor        *volume_monitor,
-                   GUnixMountEntry       *mount_entry,
-                   GUnixVolume  *volume)
+_g_unix_mount_new (GVolumeMonitor  *volume_monitor,
+                   GUnixMountEntry *mount_entry,
+                   GUnixVolume     *volume)
 {
   GUnixMount *mount;
   
   /* No volume for mount: Ignore internal things */
-  if (volume == NULL && g_unix_mount_is_system_internal (mount_entry))
+  if (volume == NULL && !g_unix_mount_guess_should_display (mount_entry))
     return NULL;
 
   mount = g_object_new (G_TYPE_UNIX_MOUNT, NULL);
   mount->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
   mount->device_path = g_strdup (g_unix_mount_get_device_path (mount_entry));
   mount->mount_path = g_strdup (g_unix_mount_get_mount_path (mount_entry));
+  mount->can_eject = g_unix_mount_guess_can_eject (mount_entry);
+
   mount->name = g_unix_mount_guess_name (mount_entry);
   mount->icon = g_unix_mount_guess_icon (mount_entry);
 
@@ -147,7 +155,7 @@ _g_unix_mount_unset_volume (GUnixMount *mount,
       /* TODO: Emit changed in idle to avoid locking issues */
       g_signal_emit_by_name (mount, "changed");
       if (mount->volume_monitor != NULL)
-        g_signal_emit_by_name (mount->volume_monitor, "mount_changed", mount);
+        g_signal_emit_by_name (mount->volume_monitor, "mount-changed", mount);
     }
 }
 
@@ -168,6 +176,12 @@ g_unix_mount_get_icon (GMount *mount)
 }
 
 static char *
+g_unix_mount_get_uuid (GMount *mount)
+{
+  return NULL;
+}
+
+static char *
 g_unix_mount_get_name (GMount *mount)
 {
   GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
@@ -210,6 +224,13 @@ g_unix_mount_can_unmount (GMount *mount)
   return TRUE;
 }
 
+static gboolean
+g_unix_mount_can_eject (GMount *mount)
+{
+  GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
+  return unix_mount->can_eject;
+}
+
 
 typedef struct {
   GUnixMount *unix_mount;
@@ -218,14 +239,15 @@ typedef struct {
   GCancellable *cancellable;
   int error_fd;
   GIOChannel *error_channel;
-  guint error_channel_source_id;
+  GSource *error_channel_source;
   GString *error_string;
-} UnmountOp;
+  gchar **argv;
+} UnmountEjectOp;
 
 static void 
-unmount_cb (GPid pid, gint status, gpointer user_data)
+eject_unmount_cb (GPid pid, gint status, gpointer user_data)
 {
-  UnmountOp *data = user_data;
+  UnmountEjectOp *data = user_data;
   GSimpleAsyncResult *simple;
   
   if (WEXITSTATUS (status) != 0)
@@ -251,55 +273,70 @@ unmount_cb (GPid pid, gint status, gpointer user_data)
   g_simple_async_result_complete (simple);
   g_object_unref (simple);
 
-  g_source_remove (data->error_channel_source_id);
+  if (data->error_channel_source)
+    {
+      g_source_destroy (data->error_channel_source);
+      g_source_unref (data->error_channel_source);
+    }
   g_io_channel_unref (data->error_channel);
   g_string_free (data->error_string, TRUE);
+  g_strfreev (data->argv);
   close (data->error_fd);
   g_spawn_close_pid (pid);
   g_free (data);
 }
 
 static gboolean
-unmount_read_error (GIOChannel *channel,
+eject_unmount_read_error (GIOChannel *channel,
                     GIOCondition condition,
                     gpointer user_data)
 {
-  char *str;
-  gsize str_len;
-  UnmountOp *data = user_data;
+  UnmountEjectOp *data = user_data;
+  char buf[BUFSIZ];
+  gsize bytes_read;
+  GError *error;
+  GIOStatus status;
+
+  error = NULL;
+read:
+  status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error);
+  if (status == G_IO_STATUS_NORMAL)
+   {
+     g_string_append_len (data->error_string, buf, bytes_read);
+     if (bytes_read == sizeof (buf))
+        goto read;
+   }
+  else if (status == G_IO_STATUS_EOF)
+    g_string_append_len (data->error_string, buf, bytes_read);
+  else if (status == G_IO_STATUS_ERROR)
+    {
+      if (data->error_string->len > 0)
+        g_string_append (data->error_string, "\n");
+
+      g_string_append (data->error_string, error->message);
+      g_error_free (error);
+
+      if (data->error_channel_source)
+        {
+          g_source_unref (data->error_channel_source);
+          data->error_channel_source = NULL;
+        }
+      return FALSE;
+    }
 
-  g_io_channel_read_to_end (channel, &str, &str_len, NULL);
-  g_string_append (data->error_string, str);
-  g_free (str);
   return TRUE;
 }
 
-static void
-g_unix_mount_unmount (GMount             *mount,
-                      GCancellable        *cancellable,
-                      GAsyncReadyCallback  callback,
-                      gpointer             user_data)
+static gboolean
+eject_unmount_do_cb (gpointer user_data)
 {
-  GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
-  UnmountOp *data;
+  UnmountEjectOp *data = (UnmountEjectOp *) user_data;
   GPid child_pid;
-  GError *error;
-  char *argv[] = {"umount", NULL, NULL};
+  GSource *child_watch;
+  GError *error = NULL;
 
-  if (unix_mount->mount_path != NULL)
-    argv[1] = unix_mount->mount_path;
-  else
-    argv[1] = unix_mount->device_path;
-  
-  data = g_new0 (UnmountOp, 1);
-  data->unix_mount = unix_mount;
-  data->callback = callback;
-  data->user_data = user_data;
-  data->cancellable = cancellable;
-  
-  error = NULL;
   if (!g_spawn_async_with_pipes (NULL,         /* working dir */
-                                 argv,
+                                 data->argv,
                                  NULL,         /* envp */
                                  G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH,
                                  NULL,         /* child_setup */
@@ -309,21 +346,91 @@ g_unix_mount_unmount (GMount             *mount,
                                  NULL,           /* standard_output */
                                  &(data->error_fd),
                                  &error)) {
+    g_assert (error != NULL);
+    goto handle_error;
+  }
+
+  data->error_string = g_string_new ("");
+
+  data->error_channel = g_io_channel_unix_new (data->error_fd);
+  g_io_channel_set_flags (data->error_channel, G_IO_FLAG_NONBLOCK, &error);
+  if (error != NULL)
+    goto handle_error;
+
+  data->error_channel_source = g_io_create_watch (data->error_channel, G_IO_IN);
+  g_source_set_callback (data->error_channel_source,
+                         (GSourceFunc) eject_unmount_read_error, data, NULL);
+  g_source_attach (data->error_channel_source, g_main_context_get_thread_default ());
+
+  child_watch = g_child_watch_source_new (child_pid);
+  g_source_set_callback (child_watch, (GSourceFunc) eject_unmount_cb, data, NULL);
+  g_source_attach (child_watch, g_main_context_get_thread_default ());
+  g_source_unref (child_watch);
+
+handle_error:
+  if (error != NULL) {
     GSimpleAsyncResult *simple;
-    simple = g_simple_async_result_new_from_error (G_OBJECT (data->unix_mount),
+    simple = g_simple_async_result_new_take_error (G_OBJECT (data->unix_mount),
                                                    data->callback,
                                                    data->user_data,
                                                    error);
     g_simple_async_result_complete (simple);
     g_object_unref (simple);
-    g_error_free (error);
+
+    if (data->error_string != NULL)
+      g_string_free (data->error_string, TRUE);
+
+    if (data->error_channel != NULL)
+      g_io_channel_unref (data->error_channel);
+
+    g_strfreev (data->argv);
     g_free (data);
-    return;
   }
-  data->error_string = g_string_new ("");
-  data->error_channel = g_io_channel_unix_new (data->error_fd);
-  data->error_channel_source_id = g_io_add_watch (data->error_channel, G_IO_IN, unmount_read_error, data);
-  g_child_watch_add (child_pid, unmount_cb, data);
+
+  return FALSE;
+}
+
+static void
+eject_unmount_do (GMount              *mount,
+                  GCancellable        *cancellable,
+                  GAsyncReadyCallback  callback,
+                  gpointer             user_data,
+                  char               **argv)
+{
+  GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
+  UnmountEjectOp *data;
+
+  data = g_new0 (UnmountEjectOp, 1);
+  data->unix_mount = unix_mount;
+  data->callback = callback;
+  data->user_data = user_data;
+  data->cancellable = cancellable;
+  data->argv = g_strdupv (argv);
+
+  if (unix_mount->volume_monitor != NULL)
+    g_signal_emit_by_name (unix_mount->volume_monitor, "mount-pre-unmount", mount);
+
+  g_signal_emit_by_name (mount, "pre-unmount", 0);
+
+  g_timeout_add (500, (GSourceFunc) eject_unmount_do_cb, data);
+}
+
+static void
+g_unix_mount_unmount (GMount             *mount,
+                      GMountUnmountFlags flags,
+                      GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
+{
+  GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
+  char *argv[] = {"umount", NULL, NULL};
+
+  if (unix_mount->mount_path != NULL)
+    argv[1] = unix_mount->mount_path;
+  else
+    argv[1] = unix_mount->device_path;
+
+  eject_unmount_do (mount, cancellable, callback, user_data, argv);
 }
 
 static gboolean
@@ -335,14 +442,44 @@ g_unix_mount_unmount_finish (GMount       *mount,
 }
 
 static void
+g_unix_mount_eject (GMount             *mount,
+                    GMountUnmountFlags flags,
+                    GCancellable        *cancellable,
+                    GAsyncReadyCallback  callback,
+                    gpointer             user_data)
+{
+  GUnixMount *unix_mount = G_UNIX_MOUNT (mount);
+  char *argv[] = {"eject", NULL, NULL};
+
+  if (unix_mount->mount_path != NULL)
+    argv[1] = unix_mount->mount_path;
+  else
+    argv[1] = unix_mount->device_path;
+
+  eject_unmount_do (mount, cancellable, callback, user_data, argv);
+}
+
+static gboolean
+g_unix_mount_eject_finish (GMount       *mount,
+                           GAsyncResult  *result,
+                           GError       **error)
+{
+  return TRUE;
+}
+
+static void
 g_unix_mount_mount_iface_init (GMountIface *iface)
 {
   iface->get_root = g_unix_mount_get_root;
   iface->get_name = g_unix_mount_get_name;
   iface->get_icon = g_unix_mount_get_icon;
+  iface->get_uuid = g_unix_mount_get_uuid;
   iface->get_drive = g_unix_mount_get_drive;
   iface->get_volume = g_unix_mount_get_volume;
   iface->can_unmount = g_unix_mount_can_unmount;
+  iface->can_eject = g_unix_mount_can_eject;
   iface->unmount = g_unix_mount_unmount;
   iface->unmount_finish = g_unix_mount_unmount_finish;
+  iface->eject = g_unix_mount_eject;
+  iface->eject_finish = g_unix_mount_eject_finish;
 }