summaryrefslogtreecommitdiff
path: root/src/udiskslinuxdrive.c
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2010-10-31 11:54:16 -0400
committerDavid Zeuthen <davidz@redhat.com>2010-10-31 11:54:16 -0400
commitba666c5800432b9250e08c68d57bf40535b2319e (patch)
tree8d085e84404260ae5138e292d93d3192572f830a /src/udiskslinuxdrive.c
parentd164a2993e5784696a597d18f7f4fb4e9154e130 (diff)
Add org.freedesktop.UDisks.Drive interfaces
This relies on a set of udev patches that sets ID_* on scsi_device sysfs devices instead of just the block devices. Signed-off-by: David Zeuthen <davidz@redhat.com>
Diffstat (limited to 'src/udiskslinuxdrive.c')
-rw-r--r--src/udiskslinuxdrive.c591
1 files changed, 591 insertions, 0 deletions
diff --git a/src/udiskslinuxdrive.c b/src/udiskslinuxdrive.c
new file mode 100644
index 0000000..7f4bfa5
--- /dev/null
+++ b/src/udiskslinuxdrive.c
@@ -0,0 +1,591 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+
+#include "udisksdaemon.h"
+#include "udisksdaemonutil.h"
+#include "udiskslinuxdrive.h"
+
+/**
+ * SECTION:udiskslinuxdrive
+ * @title: UDisksLinuxDrive
+ * @short_description: Linux drives (ATA, SCSI, etc.)
+ *
+ * Object corresponding to a drive on Linux.
+ */
+
+typedef struct _UDisksLinuxDriveClass UDisksLinuxDriveClass;
+
+/**
+ * UDisksLinuxDrive:
+ *
+ * The #UDisksLinuxDrive structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct _UDisksLinuxDrive
+{
+ GDBusObject parent_instance;
+
+ UDisksDaemon *daemon;
+
+ GUdevDevice *device;
+
+ /* interfaces */
+ UDisksDrive *iface_drive;
+ UDisksScsiDrive *iface_scsi_drive;
+ UDisksAtaDrive *iface_ata_drive;
+};
+
+struct _UDisksLinuxDriveClass
+{
+ GDBusObjectClass parent_class;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DAEMON,
+ PROP_DEVICE
+};
+
+static gboolean udisks_linux_drive_check_device (GUdevDevice *device);
+
+G_DEFINE_TYPE (UDisksLinuxDrive, udisks_linux_drive, G_TYPE_DBUS_OBJECT);
+
+static void
+udisks_linux_drive_finalize (GObject *object)
+{
+ UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (object);
+
+ /* note: we don't hold a ref to drive->daemon or drive->mount_monitor */
+
+ g_object_unref (drive->device);
+
+ if (drive->iface_drive != NULL)
+ g_object_unref (drive->iface_drive);
+ if (drive->iface_scsi_drive != NULL)
+ g_object_unref (drive->iface_scsi_drive);
+ if (drive->iface_ata_drive != NULL)
+ g_object_unref (drive->iface_ata_drive);
+
+ if (G_OBJECT_CLASS (udisks_linux_drive_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (udisks_linux_drive_parent_class)->finalize (object);
+}
+
+static void
+udisks_linux_drive_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (object);
+
+ switch (prop_id)
+ {
+ case PROP_DAEMON:
+ g_value_set_object (value, udisks_linux_drive_get_daemon (drive));
+ break;
+
+ case PROP_DEVICE:
+ g_value_set_object (value, udisks_linux_drive_get_device (drive));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+udisks_linux_drive_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (object);
+
+ switch (prop_id)
+ {
+ case PROP_DAEMON:
+ g_assert (drive->daemon == NULL);
+ /* we don't take a reference to the daemon */
+ drive->daemon = g_value_get_object (value);
+ break;
+
+ case PROP_DEVICE:
+ g_assert (drive->device == NULL);
+ drive->device = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+udisks_linux_drive_init (UDisksLinuxDrive *drive)
+{
+}
+
+static gchar *
+util_compute_object_path (const gchar *path)
+{
+ const gchar *basename;
+ GString *s;
+ guint n;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ basename = strrchr (path, '/');
+ if (basename != NULL)
+ basename++;
+ else
+ basename = path;
+
+ s = g_string_new ("/org/freedesktop/UDisks/drives/");
+ for (n = 0; basename[n] != '\0'; n++)
+ {
+ gint c = basename[n];
+
+ /* D-Bus spec sez:
+ *
+ * Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_"
+ */
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
+ {
+ g_string_append_c (s, c);
+ }
+ else
+ {
+ /* Escape bytes not in [A-Z][a-z][0-9] as _<hex-with-two-digits> */
+ g_string_append_printf (s, "_%02x", c);
+ }
+ }
+
+ return g_string_free (s, FALSE);
+}
+
+static GObjectConstructParam *
+find_construct_property (guint n_construct_properties,
+ GObjectConstructParam *construct_properties,
+ const gchar *name)
+{
+ guint n;
+ for (n = 0; n < n_construct_properties; n++)
+ if (g_strcmp0 (g_param_spec_get_name (construct_properties[n].pspec), name) == 0)
+ return &construct_properties[n];
+ return NULL;
+}
+
+/* unless given, compute object path from sysfs path */
+static GObject *
+udisks_linux_drive_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GObjectConstructParam *device_cp;
+ GObjectConstructParam *object_path_cp;
+ GUdevDevice *device;
+ gchar *object_path;
+
+ device_cp = find_construct_property (n_construct_properties, construct_properties, "device");
+ object_path_cp = find_construct_property (n_construct_properties, construct_properties, "object-path");
+ g_assert (device_cp != NULL && object_path_cp != NULL);
+
+ device = G_UDEV_DEVICE (g_value_get_object (device_cp->value));
+ g_assert (device != NULL);
+
+ if (!udisks_linux_drive_check_device (device))
+ {
+ return NULL;
+ }
+ else
+ {
+ if (g_value_get_string (object_path_cp->value) == NULL)
+ {
+ object_path = util_compute_object_path (g_udev_device_get_sysfs_path (device));
+ g_value_take_string (object_path_cp->value, object_path);
+ }
+ return G_OBJECT_CLASS (udisks_linux_drive_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties);
+ }
+}
+
+static void
+udisks_linux_drive_constructed (GObject *object)
+{
+ UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (object);
+
+ /* initial coldplug */
+ udisks_linux_drive_uevent (drive, "add", NULL);
+
+ if (G_OBJECT_CLASS (udisks_linux_drive_parent_class)->constructed != NULL)
+ G_OBJECT_CLASS (udisks_linux_drive_parent_class)->constructed (object);
+}
+
+static void
+udisks_linux_drive_class_init (UDisksLinuxDriveClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->constructor = udisks_linux_drive_constructor;
+ gobject_class->finalize = udisks_linux_drive_finalize;
+ gobject_class->constructed = udisks_linux_drive_constructed;
+ gobject_class->set_property = udisks_linux_drive_set_property;
+ gobject_class->get_property = udisks_linux_drive_get_property;
+
+ /**
+ * UDisksLinuxDrive:daemon:
+ *
+ * The #UDisksDaemon the object is for.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_DAEMON,
+ g_param_spec_object ("daemon",
+ "Daemon",
+ "The daemon the object is for",
+ UDISKS_TYPE_DAEMON,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * UDisksLinuxDrive:device:
+ *
+ * The #GUdevDevice for the object. Connect to the #GObject::notify
+ * signal to get notified whenever this is updated.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_DEVICE,
+ g_param_spec_object ("device",
+ "Device",
+ "The device for the object",
+ G_UDEV_TYPE_DEVICE,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+}
+
+/**
+ * udisks_linux_drive_new:
+ * @daemon: A #UDisksDaemon.
+ * @device: The #GUdevDevice for the sysfs drive device.
+ *
+ * Create a new drive object.
+ *
+ * Returns: A #UDisksLinuxDrive object or %NULL if @device does not represent a drive. Free with g_object_unref().
+ */
+UDisksLinuxDrive *
+udisks_linux_drive_new (UDisksDaemon *daemon,
+ GUdevDevice *device)
+{
+ GObject *object;
+
+ g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL);
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
+
+ object = g_object_new (UDISKS_TYPE_LINUX_DRIVE,
+ "daemon", daemon,
+ "device", device,
+ NULL);
+
+ if (object != NULL)
+ return UDISKS_LINUX_DRIVE (object);
+ else
+ return NULL;
+}
+
+/**
+ * udisks_linux_drive_get_daemon:
+ * @drive: A #UDisksLinuxDrive.
+ *
+ * Gets the daemon used by @drive.
+ *
+ * Returns: A #UDisksDaemon. Do not free, the object is owned by @drive.
+ */
+UDisksDaemon *
+udisks_linux_drive_get_daemon (UDisksLinuxDrive *drive)
+{
+ g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE (drive), NULL);
+ return drive->daemon;
+}
+
+/**
+ * udisks_linux_drive_get_device:
+ * @drive: A #UDisksLinuxDrive.
+ *
+ * Gets the current #GUdevDevice for @drive. Connect to
+ * #GObject::notify to track changes to the #UDisksLinuxDrive:device
+ * property.
+ *
+ * Returns: A #GUdevDevice. Free with g_object_unref().
+ */
+GUdevDevice *
+udisks_linux_drive_get_device (UDisksLinuxDrive *drive)
+{
+ g_return_val_if_fail (UDISKS_IS_LINUX_DRIVE (drive), NULL);
+ return g_object_ref (drive->device);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef gboolean (*HasInterfaceFunc) (UDisksLinuxDrive *drive);
+typedef void (*UpdateInterfaceFunc) (UDisksLinuxDrive *drive,
+ const gchar *uevent_action,
+ GDBusInterface *interface);
+
+static void
+update_iface (UDisksLinuxDrive *drive,
+ const gchar *uevent_action,
+ HasInterfaceFunc has_func,
+ UpdateInterfaceFunc update_func,
+ GType stub_type,
+ gpointer _interface_pointer)
+{
+ gboolean has;
+ gboolean add;
+ GDBusInterface **interface_pointer = _interface_pointer;
+
+ g_return_if_fail (drive != NULL);
+ g_return_if_fail (has_func != NULL);
+ g_return_if_fail (update_func != NULL);
+ g_return_if_fail (g_type_is_a (stub_type, G_TYPE_OBJECT));
+ g_return_if_fail (g_type_is_a (stub_type, G_TYPE_DBUS_INTERFACE));
+ g_return_if_fail (interface_pointer != NULL);
+ g_return_if_fail (*interface_pointer == NULL || G_IS_DBUS_INTERFACE (*interface_pointer));
+
+ add = FALSE;
+ has = has_func (drive);
+ if (*interface_pointer == NULL)
+ {
+ if (has)
+ {
+ *interface_pointer = g_object_new (stub_type, NULL);
+ add = TRUE;
+ }
+ }
+ else
+ {
+ if (!has)
+ {
+ g_dbus_object_remove_interface (G_DBUS_OBJECT (drive), G_DBUS_INTERFACE (*interface_pointer));
+ g_object_unref (*interface_pointer);
+ *interface_pointer = NULL;
+ }
+ }
+
+ if (*interface_pointer != NULL)
+ {
+ update_func (drive, uevent_action, G_DBUS_INTERFACE (*interface_pointer));
+ if (add)
+ g_dbus_object_add_interface (G_DBUS_OBJECT (drive), G_DBUS_INTERFACE (*interface_pointer));
+ }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* org.freedesktop.UDisks.Drive */
+
+static gboolean
+drive_check (UDisksLinuxDrive *drive)
+{
+ return TRUE;
+}
+
+static void
+drive_update (UDisksLinuxDrive *drive,
+ const gchar *uevent_action,
+ GDBusInterface *_iface)
+{
+ UDisksDrive *iface = UDISKS_DRIVE (_iface);
+
+ /* this is the _almost_ the same for both ATA and SCSI devices (cf. udev's ata_id and scsi_id)
+ * but we special case since there are subtle differences...
+ */
+ if (g_udev_device_get_property_as_boolean (drive->device, "ID_ATA"))
+ {
+ const gchar *model;
+ gchar *s;
+
+ model = g_udev_device_get_property (drive->device, "ID_MODEL_ENC");
+ s = udisks_decode_udev_string (model);
+ g_strstrip (s);
+ udisks_drive_set_model (iface, s);
+ g_free (s);
+
+ udisks_drive_set_vendor (iface, g_udev_device_get_property (drive->device, ""));
+ udisks_drive_set_revision (iface, g_udev_device_get_property (drive->device, "ID_REVISION"));
+ udisks_drive_set_serial (iface, g_udev_device_get_property (drive->device, "ID_SERIAL_SHORT"));
+ udisks_drive_set_wwn (iface, g_udev_device_get_property (drive->device, "ID_WWN_WITH_EXTENSION"));
+ }
+ else if (g_udev_device_get_property_as_boolean (drive->device, "ID_SCSI"))
+ {
+ const gchar *vendor;
+ const gchar *model;
+ gchar *s;
+
+ vendor = g_udev_device_get_property (drive->device, "ID_VENDOR_ENC");
+ s = udisks_decode_udev_string (vendor);
+ g_strstrip (s);
+ udisks_drive_set_vendor (iface, s);
+ g_free (s);
+
+ model = g_udev_device_get_property (drive->device, "ID_MODEL_ENC");
+ s = udisks_decode_udev_string (model);
+ g_strstrip (s);
+ udisks_drive_set_model (iface, s);
+ g_free (s);
+
+ udisks_drive_set_revision (iface, g_udev_device_get_property (drive->device, "ID_REVISION"));
+ udisks_drive_set_serial (iface, g_udev_device_get_property (drive->device, "ID_SCSI_SERIAL"));
+ udisks_drive_set_wwn (iface, g_udev_device_get_property (drive->device, "ID_WWN_WITH_EXTENSION"));
+ }
+ else
+ {
+ /* generic fallback... */
+ udisks_drive_set_vendor (iface, g_udev_device_get_property (drive->device, "ID_VENDOR"));
+ udisks_drive_set_model (iface, g_udev_device_get_property (drive->device, "ID_MODEL"));
+ udisks_drive_set_revision (iface, g_udev_device_get_property (drive->device, "ID_REVISION"));
+ udisks_drive_set_serial (iface, g_udev_device_get_property (drive->device, "ID_SERIAL_SHORT"));
+ udisks_drive_set_wwn (iface, g_udev_device_get_property (drive->device, "ID_WWN_WITH_EXTENSION"));
+ }
+
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* org.freedesktop.UDisks.AtaDrive */
+
+static gboolean
+ata_drive_check (UDisksLinuxDrive *drive)
+{
+ if (g_udev_device_get_property_as_boolean (drive->device, "ID_ATA"))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+ata_drive_update (UDisksLinuxDrive *drive,
+ const gchar *uevent_action,
+ GDBusInterface *_iface)
+{
+ //UDisksAtaDrive *iface = UDISKS_DRIVE (_iface);
+
+ /* TODO */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* org.freedesktop.UDisks.ScsiDrive */
+
+static gboolean
+scsi_drive_check (UDisksLinuxDrive *drive)
+{
+ if (g_udev_device_get_property_as_boolean (drive->device, "ID_SCSI"))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+scsi_drive_update (UDisksLinuxDrive *drive,
+ const gchar *uevent_action,
+ GDBusInterface *_iface)
+{
+ //UDisksScsiDrive *iface = UDISKS_DRIVE (_iface);
+ /* TODO */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * udisks_linux_drive_uevent:
+ * @drive: A #UDisksLinuxDrive.
+ * @action: Uevent action or %NULL
+ * @device: A new #GUdevDevice device object or %NULL if the device hasn't changed.
+ *
+ * Updates all information on interfaces on @drive.
+ */
+void
+udisks_linux_drive_uevent (UDisksLinuxDrive *drive,
+ const gchar *action,
+ GUdevDevice *device)
+{
+ g_return_if_fail (UDISKS_IS_LINUX_DRIVE (drive));
+ g_return_if_fail (device == NULL || G_UDEV_IS_DEVICE (device));
+
+ if (device != NULL)
+ {
+ g_object_unref (drive->device);
+ drive->device = g_object_ref (device);
+ g_object_notify (G_OBJECT (drive), "device");
+ }
+
+ update_iface (drive, action, drive_check, drive_update,
+ UDISKS_TYPE_DRIVE_STUB, &drive->iface_drive);
+ update_iface (drive, action, ata_drive_check, ata_drive_update,
+ UDISKS_TYPE_ATA_DRIVE_STUB, &drive->iface_ata_drive);
+ update_iface (drive, action, scsi_drive_check, scsi_drive_update,
+ UDISKS_TYPE_SCSI_DRIVE_STUB, &drive->iface_scsi_drive);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* <internal>
+ * udisks_linux_drive_check_device:
+ * @device: A #GUdevDevice.
+ *
+ * Checks if we should even construct a #UDisksLinuxDrive for @device.
+ *
+ * Returns: %TRUE if we should construct an object, %FALSE otherwise.
+ */
+static gboolean
+udisks_linux_drive_check_device (GUdevDevice *device)
+{
+ gboolean ret;
+
+ ret = FALSE;
+
+ /* The 'scsi' subsystem encompasses several objects with varying
+ * DEVTYPE including
+ *
+ * - scsi_device
+ * - scsi_target
+ * - scsi_host
+ *
+ * and we are only interested in the first.
+ */
+ if (g_strcmp0 (g_udev_device_get_devtype (device), "scsi_device") != 0)
+ goto out;
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}