Add an Eject() method to the Drive interface
authorDavid Zeuthen <davidz@redhat.com>
Thu, 4 Aug 2011 18:22:52 +0000 (14:22 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Thu, 4 Aug 2011 18:22:52 +0000 (14:22 -0400)
Signed-off-by: David Zeuthen <davidz@redhat.com>
data/org.freedesktop.UDisks2.xml
doc/udisks2-sections.txt
policy/org.freedesktop.udisks2.policy.in
src/udiskslinuxdrive.c

index 3d3514b..eac12f6 100644 (file)
       </variablelist>
     -->
     <property name="ConnectionBus" type="s" access="read"/>
+
+    <!--
+        Eject:
+        @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
+
+        Ejects media from the drive. This is only meaningful to do on
+        drives with removable media.
+
+        There are not a lot of guarantees associated with this method
+        so it should only be called in response to an user action.
+
+        On some hardware the media may be physically ejected while on
+        other hardware may simply eject the disc. On some hardware it
+        may not do anything physical but it may cause e.g. a display
+        on the hardware to show e.g. <quote>It is now safe to remove
+        the device</quote>.
+    -->
+    <method name="Eject">
+      <arg name="options" direction="in" type="a{sv}"/>
+    </method>
   </interface>
 
   <!-- ********************************************************************** -->
index 4258be9..7d766c1 100644 (file)
@@ -426,6 +426,10 @@ UDisksDrive
 UDisksDriveIface
 udisks_drive_interface_info
 udisks_drive_override_properties
+udisks_drive_call_eject
+udisks_drive_call_eject_finish
+udisks_drive_call_eject_sync
+udisks_drive_complete_eject
 udisks_drive_get_connection_bus
 udisks_drive_get_media
 udisks_drive_get_media_compatibility
index 625f593..65e8858 100644 (file)
     </defaults>
   </action>
 
-  <!-- Modify a device (create new filesystem, partitioning, change FS label, etc.) -->
+  <!-- Modify a device (create new filesystem, partitioning, change FS label, ejecting media, etc.) -->
   <action id="org.freedesktop.udisks2.modify-device">
     <_description>Modify a device</_description>
     <_message>Authentication is required to modify a device</_message>
index d2795b4..2fded2d 100644 (file)
@@ -30,6 +30,7 @@
 #include "udisksdaemonutil.h"
 #include "udiskslinuxprovider.h"
 #include "udiskslinuxdrive.h"
+#include "udiskslinuxblock.h"
 
 /**
  * SECTION:udiskslinuxdrive
@@ -369,14 +370,16 @@ udisks_linux_drive_get_devices (UDisksLinuxDrive *drive)
 /* ---------------------------------------------------------------------------------------------------- */
 
 typedef gboolean (*HasInterfaceFunc)    (UDisksLinuxDrive     *drive);
+typedef void     (*ConnectInterfaceFunc) (UDisksLinuxDrive     *drive);
 typedef void     (*UpdateInterfaceFunc) (UDisksLinuxDrive     *drive,
-                                         const gchar          *uevent_action,
-                                         GDBusInterface       *interface);
+                                         const gchar    *uevent_action,
+                                         GDBusInterface *interface);
 
 static void
-update_iface (UDisksLinuxDrive     *drive,
+update_iface (UDisksLinuxDrive           *drive,
               const gchar          *uevent_action,
               HasInterfaceFunc      has_func,
+              ConnectInterfaceFunc   connect_func,
               UpdateInterfaceFunc   update_func,
               GType                 skeleton_type,
               gpointer              _interface_pointer)
@@ -400,6 +403,8 @@ update_iface (UDisksLinuxDrive     *drive,
       if (has)
         {
           *interface_pointer = g_object_new (skeleton_type, NULL);
+          if (connect_func != NULL)
+            connect_func (drive);
           add = TRUE;
         }
     }
@@ -579,6 +584,133 @@ drive_check (UDisksLinuxDrive *drive)
   return TRUE;
 }
 
+static UDisksObject *
+find_block_object (GDBusObjectManagerServer *object_manager,
+                   UDisksLinuxDrive         *drive)
+{
+  UDisksObject *ret;
+  GList *objects;
+  GList *l;
+
+  ret = NULL;
+
+  objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
+  for (l = objects; l != NULL; l = l->next)
+    {
+      GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (l->data);
+      UDisksBlockDevice *block;
+      GUdevDevice *device;
+      gboolean is_disk;
+
+      if (!UDISKS_IS_LINUX_BLOCK (object))
+        continue;
+
+      device = udisks_linux_block_get_device (UDISKS_LINUX_BLOCK (object));
+      is_disk = (g_strcmp0 (g_udev_device_get_devtype (device), "disk") == 0);
+      g_object_unref (device);
+
+      if (!is_disk)
+        continue;
+
+      block = udisks_object_peek_block_device (UDISKS_OBJECT (object));
+
+      if (g_strcmp0 (udisks_block_device_get_drive (block),
+                     g_dbus_object_get_object_path (G_DBUS_OBJECT (drive))) == 0)
+        {
+          ret = g_object_ref (object);
+          goto out;
+        }
+    }
+
+ out:
+  g_list_foreach (objects, (GFunc) g_object_unref, NULL);
+  g_list_free (objects);
+  return ret;
+}
+
+static gboolean
+on_eject (UDisksDrive           *drive_iface,
+          GDBusMethodInvocation *invocation,
+          GVariant              *options,
+          gpointer               user_data)
+{
+  UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (user_data);
+  UDisksObject *block_object;
+  UDisksBlockDevice *block;
+  UDisksDaemon *daemon;
+  const gchar *action_id;
+  gchar *error_message;
+
+  daemon = NULL;
+  block = NULL;
+  error_message = NULL;
+
+  daemon = udisks_linux_drive_get_daemon (drive);
+  block_object = find_block_object (udisks_daemon_get_object_manager (daemon),
+                                    drive);
+  if (block_object == NULL)
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             UDISKS_ERROR,
+                                             UDISKS_ERROR_FAILED,
+                                             "Unable to find physical block device for drive");
+      goto out;
+    }
+  block = udisks_object_peek_block_device (block_object);
+
+  /* TODO: ensure it's a physical device e.g. not mpath */
+
+  /* TODO: is it a good idea to overload modify-device? */
+  action_id = "org.freedesktop.udisks2.modify-device";
+  if (udisks_block_device_get_hint_system (block))
+    action_id = "org.freedesktop.udisks2.modify-device-system";
+
+  /* Check that the user is actually authorized */
+  if (!udisks_daemon_util_check_authorization_sync (daemon,
+                                                    block_object,
+                                                    action_id,
+                                                    options,
+                                                    N_("Authentication is required to eject $(udisks2.device)"),
+                                                    invocation))
+    goto out;
+
+  if (!udisks_daemon_launch_spawned_job_sync (daemon,
+                                              NULL,  /* GCancellable */
+                                              0, /* uid_t run_as */
+                                              &error_message,
+                                              NULL,  /* input_string */
+                                              "eject \"%s\"",
+                                              udisks_block_device_get_device (block)))
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             UDISKS_ERROR,
+                                             UDISKS_ERROR_FAILED,
+                                             "Error eject %s: %s",
+                                             udisks_block_device_get_device (block),
+                                             error_message);
+      goto out;
+    }
+
+  udisks_drive_complete_eject (drive_iface, invocation);
+
+ out:
+  if (block_object != NULL)
+    g_object_unref (block_object);
+  g_free (error_message);
+  return TRUE; /* returning TRUE means that we handled the method invocation */
+}
+
+static void
+drive_connect (UDisksLinuxDrive *drive)
+{
+  g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (drive->iface_drive),
+                                       G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+  g_signal_connect (drive->iface_drive,
+                    "handle-eject",
+                    G_CALLBACK (on_eject),
+                    drive);
+}
+
 static void
 drive_set_connection_bus (UDisksLinuxDrive *drive,
                           UDisksDrive      *iface,
@@ -834,7 +966,7 @@ udisks_linux_drive_uevent (UDisksLinuxDrive *drive,
         }
     }
 
-  update_iface (drive, action, drive_check, drive_update,
+  update_iface (drive, action, drive_check, drive_connect, drive_update,
                 UDISKS_TYPE_DRIVE_SKELETON, &drive->iface_drive);
 }