From: David Zeuthen Date: Sat, 28 Nov 2009 18:47:52 +0000 (-0500) Subject: Add support for Port objects X-Git-Tag: upstream/2.1.2~743 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=df86d1bf9a1066e890beae4554e2fa45be1d54e7;p=platform%2Fupstream%2Fudisks2.git Add support for Port objects This commit only includes support for ATA ports. Support for SAS and FibreChannel will follow. --- diff --git a/data/Makefile.am b/data/Makefile.am index 3b5f12e..bea1c84 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -2,7 +2,12 @@ NULL = dbusifdir = $(datadir)/dbus-1/interfaces -dbusif_DATA = org.freedesktop.DeviceKit.Disks.xml org.freedesktop.DeviceKit.Disks.Device.xml org.freedesktop.DeviceKit.Disks.Controller.xml +dbusif_DATA = \ + org.freedesktop.DeviceKit.Disks.xml \ + org.freedesktop.DeviceKit.Disks.Device.xml \ + org.freedesktop.DeviceKit.Disks.Controller.xml \ + org.freedesktop.DeviceKit.Disks.Port.xml \ + $(NULL) servicedir = $(datadir)/dbus-1/system-services service_in_files = org.freedesktop.DeviceKit.Disks.service.in @@ -25,10 +30,8 @@ pkgconfigdir = $(datadir)/pkgconfig pkgconfig_DATA = DeviceKit-disks.pc EXTRA_DIST = \ - org.freedesktop.DeviceKit.Disks.xml \ - org.freedesktop.DeviceKit.Disks.Device.xml \ - org.freedesktop.DeviceKit.Disks.Controller.xml \ 95-devkit-disks.rules \ + $(dbusif_DATA) \ $(service_in_files) \ $(dbusconf_in_files) \ DeviceKit-disks.pc.in \ diff --git a/data/org.freedesktop.DeviceKit.Disks.Controller.xml b/data/org.freedesktop.DeviceKit.Disks.Controller.xml index 1833874..8f7128c 100644 --- a/data/org.freedesktop.DeviceKit.Disks.Controller.xml +++ b/data/org.freedesktop.DeviceKit.Disks.Controller.xml @@ -60,6 +60,34 @@ + + + Number of physical ports on the storage controller or 0 if unknown. + + + + + + The fabric used for the storage controller to communicate with other storage + targets and initiators or blank if unknown. Known values include + + + pataParallel ATA + + + sataSerial ATA + + + esataExternal Serial ATA + + + sasSerial Attached SCSI + + + TODO: include other fabrics (FC, Parallel SCSI, etc.). + + + diff --git a/data/org.freedesktop.DeviceKit.Disks.Device.xml b/data/org.freedesktop.DeviceKit.Disks.Device.xml index 900b0b4..b5684cb 100644 --- a/data/org.freedesktop.DeviceKit.Disks.Device.xml +++ b/data/org.freedesktop.DeviceKit.Disks.Device.xml @@ -2194,6 +2194,14 @@ is TRUE. + + + The object of the port for the drive or "/" if no port exists. + This property is only valid if + DeviceIsDrive + is TRUE. + + diff --git a/data/org.freedesktop.DeviceKit.Disks.Port.xml b/data/org.freedesktop.DeviceKit.Disks.Port.xml new file mode 100644 index 0000000..be3cb83 --- /dev/null +++ b/data/org.freedesktop.DeviceKit.Disks.Port.xml @@ -0,0 +1,67 @@ + + + + + + + + + + +]> + + + + + + This interface provides information about ports attached to + either an adapter or expansion device (e.g. SAS expanders or + SATA Port Multipliers). + + + + + + + + + + + Something on the port changed. + + + + + + + + + + OS specific native path of the port. On Linux this is the sysfs path, for example /sys/devices/pci0000:00/0000:00:1f.1/host3/scsi_host/host3 or TODO:SAS example. + + + + + + The object path of the controller the port is attached to. + + + + + + The object path of the adapter or expansion device the port is attached to. + + + + + + The number of the port (starting from zero) or -1 if unknown. + + + + + + diff --git a/data/org.freedesktop.DeviceKit.Disks.xml b/data/org.freedesktop.DeviceKit.Disks.xml index 66dddcd..f419427 100644 --- a/data/org.freedesktop.DeviceKit.Disks.xml +++ b/data/org.freedesktop.DeviceKit.Disks.xml @@ -33,6 +33,23 @@ + + + + An array of object paths for ports. + + + + + + Enumerate all storage ports on the system. + + + + + + + @@ -506,9 +523,6 @@ - - - Object path of controller that was removed. @@ -522,9 +536,6 @@ - - - Object path of controller that was changed. @@ -541,6 +552,48 @@ + + + Object path of port that was added. + + + + + + Emitted when a port is added. + + + + + + + Object path of port that was removed. + + + + + + Emitted when a port is removed. + + + + + + + Object path of port that was changed. + + + + + + Emitted when a port changed. + + + + + + + The version of the running daemon. diff --git a/doc/Makefile.am b/doc/Makefile.am index d8e8239..13aa4a5 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -55,6 +55,7 @@ content_files = \ dbus/org.freedesktop.DeviceKit.Disks.ref.xml \ dbus/org.freedesktop.DeviceKit.Disks.Device.ref.xml \ dbus/org.freedesktop.DeviceKit.Disks.Controller.ref.xml \ + dbus/org.freedesktop.DeviceKit.Disks.Port.ref.xml \ $(NULL) # Images to copy into HTML directory diff --git a/doc/dbus/Makefile.am b/doc/dbus/Makefile.am index 13c6144..da5ba59 100644 --- a/doc/dbus/Makefile.am +++ b/doc/dbus/Makefile.am @@ -1,5 +1,5 @@ -all : org.freedesktop.DeviceKit.Disks.ref.xml org.freedesktop.DeviceKit.Disks.Device.ref.xml org.freedesktop.DeviceKit.Disks.Controller.ref.xml +all : org.freedesktop.DeviceKit.Disks.ref.xml org.freedesktop.DeviceKit.Disks.Device.ref.xml org.freedesktop.DeviceKit.Disks.Controller.ref.xml org.freedesktop.DeviceKit.Disks.Port.ref.xml org.freedesktop.DeviceKit.Disks.ref.xml : $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl echo """" > $@ @@ -13,6 +13,10 @@ org.freedesktop.DeviceKit.Disks.Controller.ref.xml : $(top_srcdir)/data/org.free echo """" > $@ $(XSLTPROC) -nonet $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@ +org.freedesktop.DeviceKit.Disks.Port.ref.xml : $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Port.xml $(top_srcdir)/doc/dbus/spec-to-docbook.xsl + echo """" > $@ + $(XSLTPROC) -nonet $(top_srcdir)/doc/dbus/spec-to-docbook.xsl $< | tail -n +2 >> $@ + EXTRA_DIST = spec-to-docbook.xsl dbus-introspect-docs.dtd clean-local : diff --git a/doc/devkit-disks-docs.xml b/doc/devkit-disks-docs.xml index bd5b0b9..ee2d39c 100644 --- a/doc/devkit-disks-docs.xml +++ b/doc/devkit-disks-docs.xml @@ -66,6 +66,7 @@ + diff --git a/src/Makefile.am b/src/Makefile.am index b96904e..73d3252 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,6 +22,7 @@ BUILT_SOURCES = \ devkit-disks-daemon-glue.h \ devkit-disks-device-glue.h \ devkit-disks-controller-glue.h \ + devkit-disks-port-glue.h \ devkit-disks-marshal.h devkit-disks-marshal.c devkit-disks-marshal.h: devkit-disks-marshal.list @@ -39,6 +40,9 @@ devkit-disks-device-glue.h: $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.D devkit-disks-controller-glue.h: $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Controller.xml Makefile.am dbus-binding-tool --prefix=devkit_disks_controller --mode=glib-server --output=devkit-disks-controller-glue.h $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Controller.xml +devkit-disks-port-glue.h: $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Port.xml Makefile.am + dbus-binding-tool --prefix=devkit_disks_port --mode=glib-server --output=devkit-disks-port-glue.h $(top_srcdir)/data/org.freedesktop.DeviceKit.Disks.Port.xml + libexec_PROGRAMS = devkit-disks-daemon devkit_disks_daemon_SOURCES = \ @@ -49,6 +53,8 @@ devkit_disks_daemon_SOURCES = \ devkit-disks-device-private.h devkit-disks-device-private.c \ devkit-disks-controller.h devkit-disks-controller.c \ devkit-disks-controller-private.h devkit-disks-controller-private.c \ + devkit-disks-port.h devkit-disks-port.c \ + devkit-disks-port-private.h devkit-disks-port-private.c \ devkit-disks-mount-file.h devkit-disks-mount-file.c \ devkit-disks-mount.h devkit-disks-mount.c \ devkit-disks-mount-monitor.h devkit-disks-mount-monitor.c \ diff --git a/src/devkit-disks-controller-private.c b/src/devkit-disks-controller-private.c index 40a5ec0..12621e4 100644 --- a/src/devkit-disks-controller-private.c +++ b/src/devkit-disks-controller-private.c @@ -96,3 +96,24 @@ devkit_disks_controller_set_driver (DevkitDisksController *controller, const gch emit_changed (controller, "driver"); } } + +void +devkit_disks_controller_set_num_ports (DevkitDisksController *controller, guint value) +{ + if (G_UNLIKELY (controller->priv->num_ports != value)) + { + controller->priv->num_ports = value; + emit_changed (controller, "num_ports"); + } +} + +void +devkit_disks_controller_set_fabric (DevkitDisksController *controller, const gchar *value) +{ + if (G_UNLIKELY (g_strcmp0 (controller->priv->fabric, value) != 0)) + { + g_free (controller->priv->fabric); + controller->priv->fabric = g_strdup (value); + emit_changed (controller, "fabric"); + } +} diff --git a/src/devkit-disks-controller-private.h b/src/devkit-disks-controller-private.h index e2e4b64..6fd582c 100644 --- a/src/devkit-disks-controller-private.h +++ b/src/devkit-disks-controller-private.h @@ -49,6 +49,8 @@ struct DevkitDisksControllerPrivate gchar *vendor; gchar *model; gchar *driver; + guint num_ports; + gchar *fabric; }; /* property setters */ @@ -56,6 +58,8 @@ struct DevkitDisksControllerPrivate void devkit_disks_controller_set_vendor (DevkitDisksController *controller, const gchar *value); void devkit_disks_controller_set_model (DevkitDisksController *controller, const gchar *value); void devkit_disks_controller_set_driver (DevkitDisksController *controller, const gchar *value); +void devkit_disks_controller_set_num_ports (DevkitDisksController *controller, guint value); +void devkit_disks_controller_set_fabric (DevkitDisksController *controller, const gchar *value); G_END_DECLS diff --git a/src/devkit-disks-controller.c b/src/devkit-disks-controller.c index 26c5c26..2280897 100644 --- a/src/devkit-disks-controller.c +++ b/src/devkit-disks-controller.c @@ -56,6 +56,8 @@ enum PROP_VENDOR, PROP_MODEL, PROP_DRIVER, + PROP_NUM_PORTS, + PROP_FABRIC, }; enum @@ -95,6 +97,14 @@ get_property (GObject *object, g_value_set_string (value, controller->priv->driver); break; + case PROP_NUM_PORTS: + g_value_set_uint (value, controller->priv->num_ports); + break; + + case PROP_FABRIC: + g_value_set_string (value, controller->priv->fabric); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -138,6 +148,14 @@ devkit_disks_controller_class_init (DevkitDisksControllerClass *klass) object_class, PROP_DRIVER, g_param_spec_string ("driver", NULL, NULL, NULL, G_PARAM_READABLE)); + g_object_class_install_property ( + object_class, + PROP_NUM_PORTS, + g_param_spec_uint ("num-ports", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE)); + g_object_class_install_property ( + object_class, + PROP_FABRIC, + g_param_spec_string ("fabric", NULL, NULL, NULL, G_PARAM_READABLE)); } static void diff --git a/src/devkit-disks-daemon.c b/src/devkit-disks-daemon.c index 96e2609..9ec2aed 100644 --- a/src/devkit-disks-daemon.c +++ b/src/devkit-disks-daemon.c @@ -65,6 +65,8 @@ #include "devkit-disks-device-private.h" #include "devkit-disks-controller.h" #include "devkit-disks-controller-private.h" +#include "devkit-disks-port.h" +#include "devkit-disks-port-private.h" #include "devkit-disks-mount-file.h" #include "devkit-disks-mount.h" #include "devkit-disks-mount-monitor.h" @@ -95,6 +97,9 @@ enum CONTROLLER_ADDED_SIGNAL, CONTROLLER_REMOVED_SIGNAL, CONTROLLER_CHANGED_SIGNAL, + PORT_ADDED_SIGNAL, + PORT_REMOVED_SIGNAL, + PORT_CHANGED_SIGNAL, LAST_SIGNAL, }; @@ -119,6 +124,9 @@ struct DevkitDisksDaemonPrivate GHashTable *map_native_path_to_controller; GHashTable *map_object_path_to_controller; + GHashTable *map_native_path_to_port; + GHashTable *map_object_path_to_port; + DevkitDisksMountMonitor *mount_monitor; guint ata_smart_refresh_timer_id; @@ -533,6 +541,33 @@ devkit_disks_daemon_class_init (DevkitDisksDaemonClass *klass) g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH); + signals[PORT_ADDED_SIGNAL] = + g_signal_new ("port-added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH); + + signals[PORT_REMOVED_SIGNAL] = + g_signal_new ("port-removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH); + + signals[PORT_CHANGED_SIGNAL] = + g_signal_new ("port-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, DBUS_TYPE_G_OBJECT_PATH); + dbus_g_object_type_install_info (DEVKIT_DISKS_TYPE_DAEMON, &dbus_glib_devkit_disks_daemon_object_info); @@ -591,6 +626,14 @@ devkit_disks_daemon_init (DevkitDisksDaemon *daemon) g_str_equal, g_free, g_object_unref); + daemon->priv->map_native_path_to_port = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + daemon->priv->map_object_path_to_port = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); } static void @@ -636,6 +679,12 @@ devkit_disks_daemon_finalize (GObject *object) if (daemon->priv->map_object_path_to_controller != NULL) { g_hash_table_unref (daemon->priv->map_object_path_to_controller); } + if (daemon->priv->map_native_path_to_port != NULL) { + g_hash_table_unref (daemon->priv->map_native_path_to_port); + } + if (daemon->priv->map_object_path_to_port != NULL) { + g_hash_table_unref (daemon->priv->map_object_path_to_port); + } if (daemon->priv->mount_monitor != NULL) { @@ -709,7 +758,7 @@ pci_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesi if (controller != NULL) { gboolean keep_controller; - g_print ("**** CHANGING %s\n", native_path); + g_print ("**** pci CHANGING %s\n", native_path); /* The sysfs path ('move' uevent) may actually change so remove it and add * it back after processing. The kernel name will never change so the object @@ -732,17 +781,66 @@ pci_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesi g_object_ref (controller)); if (!keep_controller) { - g_print ("**** CHANGE TRIGGERED REMOVE %s\n", native_path); + g_print ("**** pci CHANGE TRIGGERED REMOVE %s\n", native_path); device_remove (daemon, d); } else { - g_print ("**** CHANGED %s\n", native_path); + g_print ("**** pci CHANGED %s\n", native_path); } } else { - g_print ("**** TREATING CHANGE AS ADD %s\n", native_path); + g_print ("**** pci TREATING CHANGE AS ADD %s\n", native_path); + device_add (daemon, d, TRUE); + } +} + +/* ------------------------------ */ + +static void +scsi_host_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesized) +{ + DevkitDisksPort *port; + const char *native_path; + + native_path = g_udev_device_get_sysfs_path (d); + port = g_hash_table_lookup (daemon->priv->map_native_path_to_port, native_path); + if (port != NULL) { + gboolean keep_port; + + g_print ("**** scsi_host CHANGING %s\n", native_path); + + /* The sysfs path ('move' uevent) may actually change so remove it and add + * it back after processing. The kernel name will never change so the object + * path will fortunately remain constant. + */ + g_warn_if_fail (g_hash_table_remove (daemon->priv->map_native_path_to_port, + port->priv->native_path)); + + keep_port = devkit_disks_port_changed (port, d, synthesized); + + g_assert (devkit_disks_port_local_get_native_path (port) != NULL); + g_assert (g_strcmp0 (native_path, devkit_disks_port_local_get_native_path (port)) == 0); + + /* now add the things back to the global hashtables - it's important that we + * do this *before* calling port_remove() - otherwise it will never remove + * the port + */ + g_hash_table_insert (daemon->priv->map_native_path_to_port, + g_strdup (devkit_disks_port_local_get_native_path (port)), + g_object_ref (port)); + + if (!keep_port) { + g_print ("**** scsi_host CHANGE TRIGGERED REMOVE %s\n", native_path); + device_remove (daemon, d); + } else { + g_print ("**** scsi_host CHANGED %s\n", native_path); + } + } else { + g_print ("**** scsi_host TREATING CHANGE AS ADD %s\n", native_path); device_add (daemon, d, TRUE); } } +/* ------------------------------ */ + static void block_device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesized) { @@ -807,6 +905,8 @@ device_changed (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean synthesized) block_device_changed (daemon, d, synthesized); else if (g_strcmp0 (subsystem, "pci") == 0) pci_device_changed (daemon, d, synthesized); + else if (g_strcmp0 (subsystem, "scsi_host") == 0) + scsi_host_device_changed (daemon, d, synthesized); else g_warning ("Unhandled changed event from subsystem `%s'", subsystem); } @@ -844,10 +944,10 @@ pci_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event) controller = g_hash_table_lookup (daemon->priv->map_native_path_to_controller, native_path); if (controller != NULL) { /* we already have the controller; treat as change event */ - g_print ("**** TREATING ADD AS CHANGE %s\n", native_path); + g_print ("**** pci TREATING ADD AS CHANGE %s\n", native_path); device_changed (daemon, d, FALSE); } else { - g_print ("**** ADDING %s\n", native_path); + g_print ("**** pci ADDING %s\n", native_path); controller = devkit_disks_controller_new (daemon, d); if (controller != NULL) { @@ -862,19 +962,64 @@ pci_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event) g_hash_table_insert (daemon->priv->map_object_path_to_controller, g_strdup (devkit_disks_controller_local_get_object_path (controller)), g_object_ref (controller)); - g_print ("**** ADDED %s\n", native_path); + g_print ("**** pci ADDED %s\n", native_path); if (emit_event) { const char *object_path; object_path = devkit_disks_controller_local_get_object_path (controller); - g_print ("**** EMITTING ADDED for %s\n", controller->priv->native_path); + g_print ("**** pci EMITTING ADDED for %s\n", controller->priv->native_path); g_signal_emit (daemon, signals[CONTROLLER_ADDED_SIGNAL], 0, object_path); } } else { - g_print ("**** IGNORING ADD %s\n", native_path); + g_print ("**** pci IGNORING ADD %s\n", native_path); } } } +/* ------------------------------ */ + +static void +scsi_host_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event) +{ + DevkitDisksPort *port; + const char *native_path; + + native_path = g_udev_device_get_sysfs_path (d); + port = g_hash_table_lookup (daemon->priv->map_native_path_to_port, native_path); + if (port != NULL) { + /* we already have the port; treat as change event */ + g_print ("**** scsi_host TREATING ADD AS CHANGE %s\n", native_path); + device_changed (daemon, d, FALSE); + } else { + g_print ("**** scsi_host ADDING %s\n", native_path); + port = devkit_disks_port_new (daemon, d); + + if (port != NULL) { + /* assert that the port is fully loaded with info */ + g_assert (devkit_disks_port_local_get_native_path (port) != NULL); + g_assert (devkit_disks_port_local_get_object_path (port) != NULL); + g_assert (g_strcmp0 (native_path, devkit_disks_port_local_get_native_path (port)) == 0); + + g_hash_table_insert (daemon->priv->map_native_path_to_port, + g_strdup (devkit_disks_port_local_get_native_path (port)), + g_object_ref (port)); + g_hash_table_insert (daemon->priv->map_object_path_to_port, + g_strdup (devkit_disks_port_local_get_object_path (port)), + g_object_ref (port)); + g_print ("**** scsi_host ADDED %s\n", native_path); + if (emit_event) { + const char *object_path; + object_path = devkit_disks_port_local_get_object_path (port); + g_print ("**** scsi_host EMITTING ADDED for %s\n", port->priv->native_path); + g_signal_emit (daemon, signals[PORT_ADDED_SIGNAL], 0, object_path); + } + } else { + g_print ("**** scsi_host IGNORING ADD %s\n", native_path); + } + } +} + +/* ------------------------------ */ + static void block_device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event) { @@ -935,6 +1080,8 @@ device_add (DevkitDisksDaemon *daemon, GUdevDevice *d, gboolean emit_event) block_device_add (daemon, d, emit_event); else if (g_strcmp0 (subsystem, "pci") == 0) pci_device_add (daemon, d, emit_event); + else if (g_strcmp0 (subsystem, "scsi_host") == 0) + scsi_host_device_add (daemon, d, emit_event); else g_warning ("Unhandled add event from subsystem `%s'", subsystem); } @@ -950,9 +1097,9 @@ pci_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d) native_path = g_udev_device_get_sysfs_path (d); controller = g_hash_table_lookup (daemon->priv->map_native_path_to_controller, native_path); if (controller == NULL) { - g_print ("**** IGNORING REMOVE %s\n", native_path); + g_print ("**** pci IGNORING REMOVE %s\n", native_path); } else { - g_print ("**** REMOVING %s\n", native_path); + g_print ("**** pci REMOVING %s\n", native_path); g_warn_if_fail (g_strcmp0 (native_path, controller->priv->native_path) == 0); @@ -961,7 +1108,7 @@ pci_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d) g_warn_if_fail (g_hash_table_remove (daemon->priv->map_object_path_to_controller, controller->priv->object_path)); - g_print ("**** EMITTING REMOVED for %s\n", controller->priv->native_path); + g_print ("**** pci EMITTING REMOVED for %s\n", controller->priv->native_path); g_signal_emit (daemon, signals[CONTROLLER_REMOVED_SIGNAL], 0, devkit_disks_controller_local_get_object_path (controller)); @@ -971,6 +1118,40 @@ pci_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d) } } +/* ------------------------------ */ + +static void +scsi_host_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d) +{ + DevkitDisksPort *port; + const char *native_path; + + native_path = g_udev_device_get_sysfs_path (d); + port = g_hash_table_lookup (daemon->priv->map_native_path_to_port, native_path); + if (port == NULL) { + g_print ("**** scsi_host IGNORING REMOVE %s\n", native_path); + } else { + g_print ("**** scsi_host REMOVING %s\n", native_path); + + g_warn_if_fail (g_strcmp0 (native_path, port->priv->native_path) == 0); + + g_hash_table_remove (daemon->priv->map_native_path_to_port, + port->priv->native_path); + g_warn_if_fail (g_hash_table_remove (daemon->priv->map_object_path_to_port, + port->priv->object_path)); + + g_print ("**** scsi_host EMITTING REMOVED for %s\n", port->priv->native_path); + g_signal_emit (daemon, signals[PORT_REMOVED_SIGNAL], 0, + devkit_disks_port_local_get_object_path (port)); + + devkit_disks_port_removed (port); + + g_object_unref (port); + } +} + +/* ------------------------------ */ + static void block_device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d) { @@ -1026,6 +1207,8 @@ device_remove (DevkitDisksDaemon *daemon, GUdevDevice *d) block_device_remove (daemon, d); else if (g_strcmp0 (subsystem, "pci") == 0) pci_device_remove (daemon, d); + else if (g_strcmp0 (subsystem, "scsi_host") == 0) + scsi_host_device_remove (daemon, d); else g_warning ("Unhandled remove event from subsystem `%s'", subsystem); } @@ -1194,7 +1377,10 @@ register_disks_daemon (DevkitDisksDaemon *daemon) DBusConnection *connection; DBusError dbus_error; GError *error = NULL; - const char *subsystems[] = {"block", "pci", NULL}; + const char *subsystems[] = {"block", /* Disks and partitions */ + "pci", /* Storage controllers */ + "scsi_host", /* ATA ports are represented by scsi_host */ + NULL}; daemon->priv->authority = polkit_authority_get (); @@ -1290,7 +1476,7 @@ devkit_disks_daemon_new (void) goto error; } - /* process controllers */ + /* process storage controllers */ devices = g_udev_client_query_by_subsystem (daemon->priv->gudev_client, "pci"); for (l = devices; l != NULL; l = l->next) { GUdevDevice *device = l->data; @@ -1299,7 +1485,16 @@ devkit_disks_daemon_new (void) g_list_foreach (devices, (GFunc) g_object_unref, NULL); g_list_free (devices); - /* process block devices */ + /* process ATA ports */ + devices = g_udev_client_query_by_subsystem (daemon->priv->gudev_client, "scsi_host"); + for (l = devices; l != NULL; l = l->next) { + GUdevDevice *device = l->data; + device_add (daemon, device, FALSE); + } + g_list_foreach (devices, (GFunc) g_object_unref, NULL); + g_list_free (devices); + + /* process block devices (disks and partitions) */ devices = g_udev_client_query_by_subsystem (daemon->priv->gudev_client, "block"); for (l = devices; l != NULL; l = l->next) { GUdevDevice *device = l->data; @@ -1837,7 +2032,7 @@ devkit_disks_daemon_local_update_spindown (DevkitDisksDaemon *daemon) DevkitDisksController * devkit_disks_daemon_local_find_controller (DevkitDisksDaemon *daemon, - const gchar *child_native_path) + const gchar *device_native_path) { GHashTableIter iter; const gchar *native_path; @@ -1848,7 +2043,7 @@ devkit_disks_daemon_local_find_controller (DevkitDisksDaemon *daemon, g_hash_table_iter_init (&iter, daemon->priv->map_native_path_to_controller); while (g_hash_table_iter_next (&iter, (gpointer) &native_path, (gpointer) &controller)) { - if (g_str_has_prefix (child_native_path, + if (g_str_has_prefix (device_native_path, devkit_disks_controller_local_get_native_path (controller))) { ret = controller; break; @@ -1858,6 +2053,29 @@ devkit_disks_daemon_local_find_controller (DevkitDisksDaemon *daemon, return ret; } +DevkitDisksPort * +devkit_disks_daemon_local_find_port (DevkitDisksDaemon *daemon, + const gchar *device_native_path) +{ + GHashTableIter iter; + const gchar *native_path; + DevkitDisksPort *port; + DevkitDisksPort *ret; + + ret = NULL; + + g_hash_table_iter_init (&iter, daemon->priv->map_native_path_to_port); + while (g_hash_table_iter_next (&iter, (gpointer) &native_path, (gpointer) &port)) { + + if (devkit_disks_local_port_is_for_device (port, device_native_path)) { + ret = port; + break; + } + } + + return ret; +} + /*--------------------------------------------------------------------------------------------------------------*/ /* exported methods */ @@ -1917,6 +2135,32 @@ devkit_disks_daemon_enumerate_controllers (DevkitDisksDaemon *daemon, /*--------------------------------------------------------------------------------------------------------------*/ static void +enumerate_port_cb (gpointer key, gpointer value, gpointer user_data) +{ + DevkitDisksPort *port = DEVKIT_DISKS_PORT (value); + GPtrArray *object_paths = user_data; + g_ptr_array_add (object_paths, g_strdup (devkit_disks_port_local_get_object_path (port))); +} + +/* dbus-send --system --print-reply --dest=org.freedesktop.DeviceKit.Disks /org/freedesktop/DeviceKit/Disks org.freedesktop.DeviceKit.Disks.EnumeratePorts + */ +gboolean +devkit_disks_daemon_enumerate_ports (DevkitDisksDaemon *daemon, + DBusGMethodInvocation *context) +{ + GPtrArray *object_paths; + + object_paths = g_ptr_array_new (); + g_hash_table_foreach (daemon->priv->map_native_path_to_port, enumerate_port_cb, object_paths); + dbus_g_method_return (context, object_paths); + g_ptr_array_foreach (object_paths, (GFunc) g_free, NULL); + g_ptr_array_free (object_paths, TRUE); + return TRUE; +} + +/*--------------------------------------------------------------------------------------------------------------*/ + +static void enumerate_device_files_cb (gpointer key, gpointer value, gpointer user_data) { DevkitDisksDevice *device = DEVKIT_DISKS_DEVICE (value); diff --git a/src/devkit-disks-daemon.h b/src/devkit-disks-daemon.h index 95e15e0..3e6748a 100644 --- a/src/devkit-disks-daemon.h +++ b/src/devkit-disks-daemon.h @@ -91,7 +91,9 @@ DevkitDisksDevice *devkit_disks_daemon_local_find_by_dev (DevkitDisksDae dev_t dev); DevkitDisksController *devkit_disks_daemon_local_find_controller (DevkitDisksDaemon *daemon, - const gchar *child_native_path); + const gchar *device_native_path); +DevkitDisksPort *devkit_disks_daemon_local_find_port (DevkitDisksDaemon *daemon, + const gchar *device_native_path); typedef void (*DevkitDisksCheckAuthCallback) (DevkitDisksDaemon *daemon, DevkitDisksDevice *device, @@ -157,6 +159,9 @@ const DevkitDisksFilesystem *devkit_disks_daemon_local_get_fs_details (DevkitDis gboolean devkit_disks_daemon_enumerate_controllers (DevkitDisksDaemon *daemon, DBusGMethodInvocation *context); +gboolean devkit_disks_daemon_enumerate_ports (DevkitDisksDaemon *daemon, + DBusGMethodInvocation *context); + gboolean devkit_disks_daemon_enumerate_devices (DevkitDisksDaemon *daemon, DBusGMethodInvocation *context); diff --git a/src/devkit-disks-device-private.c b/src/devkit-disks-device-private.c index 4f1bdaf..3e1eb9c 100644 --- a/src/devkit-disks-device-private.c +++ b/src/devkit-disks-device-private.c @@ -806,6 +806,17 @@ devkit_disks_device_set_drive_controller (DevkitDisksDevice *device, const gchar } void +devkit_disks_device_set_drive_port (DevkitDisksDevice *device, const gchar *value) +{ + if (G_UNLIKELY (g_strcmp0 (device->priv->drive_port, value) != 0)) + { + g_free (device->priv->drive_port); + device->priv->drive_port = g_strdup (value); + emit_changed (device, "drive_port"); + } +} + +void devkit_disks_device_set_optical_disc_is_blank (DevkitDisksDevice *device, gboolean value) { if (G_UNLIKELY (device->priv->optical_disc_is_blank != value)) diff --git a/src/devkit-disks-device-private.h b/src/devkit-disks-device-private.h index 6c9611e..1e7b3bc 100644 --- a/src/devkit-disks-device-private.h +++ b/src/devkit-disks-device-private.h @@ -160,6 +160,7 @@ struct DevkitDisksDevicePrivate guint drive_rotation_rate; char *drive_write_cache; char *drive_controller; + char *drive_port; gboolean optical_disc_is_blank; gboolean optical_disc_is_appendable; @@ -283,6 +284,7 @@ void devkit_disks_device_set_drive_is_rotational (DevkitDisksDevice *device, gbo void devkit_disks_device_set_drive_rotation_rate (DevkitDisksDevice *device, guint value); void devkit_disks_device_set_drive_write_cache (DevkitDisksDevice *device, const gchar *value); void devkit_disks_device_set_drive_controller (DevkitDisksDevice *device, const gchar *value); +void devkit_disks_device_set_drive_port (DevkitDisksDevice *device, const gchar *value); void devkit_disks_device_set_optical_disc_is_blank (DevkitDisksDevice *device, gboolean value); void devkit_disks_device_set_optical_disc_is_appendable (DevkitDisksDevice *device, gboolean value); diff --git a/src/devkit-disks-device.c b/src/devkit-disks-device.c index 038841d..f2789b8 100644 --- a/src/devkit-disks-device.c +++ b/src/devkit-disks-device.c @@ -60,6 +60,7 @@ #include "devkit-disks-inhibitor.h" #include "devkit-disks-poller.h" #include "devkit-disks-controller.h" +#include "devkit-disks-port.h" /*--------------------------------------------------------------------------------------------------------------*/ #include "devkit-disks-device-glue.h" @@ -218,6 +219,7 @@ enum PROP_DRIVE_ROTATION_RATE, PROP_DRIVE_WRITE_CACHE, PROP_DRIVE_CONTROLLER, + PROP_DRIVE_PORT, PROP_OPTICAL_DISC_IS_BLANK, PROP_OPTICAL_DISC_IS_APPENDABLE, @@ -533,6 +535,12 @@ get_property (GObject *object, else g_value_set_boxed (value, "/"); break; + case PROP_DRIVE_PORT: + if (device->priv->drive_port != NULL) + g_value_set_boxed (value, device->priv->drive_port); + else + g_value_set_boxed (value, "/"); + break; case PROP_OPTICAL_DISC_IS_BLANK: g_value_set_boolean (value, device->priv->optical_disc_is_blank); @@ -1003,6 +1011,10 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass) object_class, PROP_DRIVE_CONTROLLER, g_param_spec_boxed ("drive-controller", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE)); + g_object_class_install_property ( + object_class, + PROP_DRIVE_PORT, + g_param_spec_boxed ("drive-port", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE)); g_object_class_install_property ( object_class, @@ -1239,6 +1251,7 @@ devkit_disks_device_finalize (GObject *object) g_free (device->priv->drive_media); g_free (device->priv->drive_write_cache); g_free (device->priv->drive_controller); + g_free (device->priv->drive_port); g_free (device->priv->linux_md_component_level); g_free (device->priv->linux_md_component_uuid); @@ -2928,9 +2941,9 @@ update_info_media_detection (DevkitDisksDevice *device) /* ---------------------------------------------------------------------------------------------------- */ -/* driver_controller property */ +/* drive_controller property */ static gboolean -update_info_driver_controller (DevkitDisksDevice *device) +update_info_drive_controller (DevkitDisksDevice *device) { DevkitDisksController *controller; const gchar *controller_object_path; @@ -2948,6 +2961,26 @@ update_info_driver_controller (DevkitDisksDevice *device) return TRUE; } +/* drive_port property */ +static gboolean +update_info_drive_port (DevkitDisksDevice *device) +{ + DevkitDisksPort *port; + const gchar *port_object_path; + + port_object_path = NULL; + + port = devkit_disks_daemon_local_find_port (device->priv->daemon, + device->priv->native_path); + if (port != NULL) { + port_object_path = devkit_disks_port_local_get_object_path (port); + } + + devkit_disks_device_set_drive_port (device, port_object_path); + + return TRUE; +} + /* ---------------------------------------------------------------------------------------------------- */ typedef struct { @@ -3342,8 +3375,12 @@ update_info (DevkitDisksDevice *device) if (!update_info_media_detection (device)) goto out; - /* device_controller proprety */ - if (!update_info_driver_controller (device)) + /* drive_controller proprety */ + if (!update_info_drive_controller (device)) + goto out; + + /* drive_port proprety */ + if (!update_info_drive_port (device)) goto out; ret = TRUE; diff --git a/src/devkit-disks-port-private.c b/src/devkit-disks-port-private.c new file mode 100644 index 0000000..f02edec --- /dev/null +++ b/src/devkit-disks-port-private.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009 David Zeuthen + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "devkit-disks-port.h" +#include "devkit-disks-port-private.h" + +static gboolean +emit_changed_idle_cb (gpointer data) +{ + DevkitDisksPort *port = DEVKIT_DISKS_PORT (data); + + //g_debug ("XXX emitting 'changed' in idle"); + + if (!port->priv->removed) + { + g_print ("**** EMITTING CHANGED for %s\n", port->priv->native_path); + g_signal_emit_by_name (port->priv->daemon, + "port-changed", + port->priv->object_path); + g_signal_emit_by_name (port, "changed"); + } + port->priv->emit_changed_idle_id = 0; + + /* remove the idle source */ + return FALSE; +} + +static void +emit_changed (DevkitDisksPort *port, const gchar *name) +{ + //g_debug ("property %s changed for %s", name, port->priv->port_file); + + if (port->priv->object_path != NULL) + { + /* schedule a 'changed' signal in idle if one hasn't been scheduled already */ + if (port->priv->emit_changed_idle_id == 0) + { + port->priv->emit_changed_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT, + emit_changed_idle_cb, + g_object_ref (port), + (GDestroyNotify) g_object_unref); + } + } +} + +void +devkit_disks_port_set_controller (DevkitDisksPort *port, const gchar *value) +{ + if (G_UNLIKELY (g_strcmp0 (port->priv->controller, value) != 0)) + { + g_free (port->priv->controller); + port->priv->controller = g_strdup (value); + emit_changed (port, "controller"); + } +} + +void +devkit_disks_port_set_parent (DevkitDisksPort *port, const gchar *value) +{ + if (G_UNLIKELY (g_strcmp0 (port->priv->parent, value) != 0)) + { + g_free (port->priv->parent); + port->priv->parent = g_strdup (value); + emit_changed (port, "parent"); + } +} + +void +devkit_disks_port_set_number (DevkitDisksPort *port, gint value) +{ + if (G_UNLIKELY (port->priv->number != value)) + { + port->priv->number = value; + emit_changed (port, "number"); + } +} diff --git a/src/devkit-disks-port-private.h b/src/devkit-disks-port-private.h new file mode 100644 index 0000000..f53d8d9 --- /dev/null +++ b/src/devkit-disks-port-private.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 David Zeuthen + * + * 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 + * + */ + +#ifndef __DEVKIT_DISKS_PORT_PRIVATE_H__ +#define __DEVKIT_DISKS_PORT_PRIVATE_H__ + +#include +#include +#include + +#include "devkit-disks-types.h" + +G_BEGIN_DECLS + +struct DevkitDisksPortPrivate +{ + DBusGConnection *system_bus_connection; + DevkitDisksDaemon *daemon; + GUdevDevice *d; + + gchar *object_path; + gchar *native_path; + gboolean removed; + + /* if non-zero, the id of the idle for emitting a 'change' signal */ + guint emit_changed_idle_id; + + gchar *native_path_for_device_prefix; + + /**************/ + /* Properties */ + /**************/ + + gchar *controller; + gchar *parent; + gint number; +}; + +/* property setters */ + +void devkit_disks_port_set_controller (DevkitDisksPort *port, const gchar *value); +void devkit_disks_port_set_parent (DevkitDisksPort *port, const gchar *value); +void devkit_disks_port_set_number (DevkitDisksPort *port, gint value); + +G_END_DECLS + +#endif /* __DEVKIT_DISKS_PORT_PRIVATE_H__ */ diff --git a/src/devkit-disks-port.c b/src/devkit-disks-port.c new file mode 100644 index 0000000..943b26c --- /dev/null +++ b/src/devkit-disks-port.c @@ -0,0 +1,587 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 David Zeuthen + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devkit-disks-daemon.h" +#include "devkit-disks-port.h" +#include "devkit-disks-port-private.h" +#include "devkit-disks-marshal.h" + +#include "devkit-disks-controller.h" + +/*--------------------------------------------------------------------------------------------------------------*/ +#include "devkit-disks-port-glue.h" + +static void devkit_disks_port_class_init (DevkitDisksPortClass *klass); +static void devkit_disks_port_init (DevkitDisksPort *seat); +static void devkit_disks_port_finalize (GObject *object); + +static gboolean update_info (DevkitDisksPort *port); + +static void drain_pending_changes (DevkitDisksPort *port, gboolean force_update); + +enum +{ + PROP_0, + PROP_NATIVE_PATH, + + PROP_CONTROLLER, + PROP_PARENT, + PROP_NUMBER, +}; + +enum +{ + CHANGED_SIGNAL, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (DevkitDisksPort, devkit_disks_port, G_TYPE_OBJECT) + +#define DEVKIT_DISKS_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPortPrivate)) + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + DevkitDisksPort *port = DEVKIT_DISKS_PORT (object); + + switch (prop_id) { + case PROP_NATIVE_PATH: + g_value_set_string (value, port->priv->native_path); + break; + + case PROP_CONTROLLER: + if (port->priv->controller != NULL) + g_value_set_boxed (value, port->priv->controller); + else + g_value_set_boxed (value, "/"); + break; + + case PROP_PARENT: + if (port->priv->parent != NULL) + g_value_set_boxed (value, port->priv->parent); + else + g_value_set_boxed (value, "/"); + break; + + case PROP_NUMBER: + g_value_set_int (value, port->priv->number); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +devkit_disks_port_class_init (DevkitDisksPortClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = devkit_disks_port_finalize; + object_class->get_property = get_property; + + g_type_class_add_private (klass, sizeof (DevkitDisksPortPrivate)); + + signals[CHANGED_SIGNAL] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + dbus_g_object_type_install_info (DEVKIT_DISKS_TYPE_PORT, &dbus_glib_devkit_disks_port_object_info); + + g_object_class_install_property ( + object_class, + PROP_NATIVE_PATH, + g_param_spec_string ("native-path", NULL, NULL, NULL, G_PARAM_READABLE)); + g_object_class_install_property ( + object_class, + PROP_CONTROLLER, + g_param_spec_boxed ("controller", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE)); + g_object_class_install_property ( + object_class, + PROP_PARENT, + g_param_spec_boxed ("parent", NULL, NULL, DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE)); + g_object_class_install_property ( + object_class, + PROP_NUMBER, + g_param_spec_int ("number", NULL, NULL, G_MININT, G_MAXINT, -1, G_PARAM_READABLE)); +} + +static void +devkit_disks_port_init (DevkitDisksPort *port) +{ + port->priv = DEVKIT_DISKS_PORT_GET_PRIVATE (port); + port->priv->number = -1; +} + +static void +devkit_disks_port_finalize (GObject *object) +{ + DevkitDisksPort *port; + + g_return_if_fail (object != NULL); + g_return_if_fail (DEVKIT_DISKS_IS_PORT (object)); + + port = DEVKIT_DISKS_PORT (object); + g_return_if_fail (port->priv != NULL); + + /* g_debug ("finalizing %s", port->priv->native_path); */ + + g_object_unref (port->priv->d); + g_object_unref (port->priv->daemon); + g_free (port->priv->object_path); + + g_free (port->priv->native_path); + g_free (port->priv->native_path_for_device_prefix); + + if (port->priv->emit_changed_idle_id > 0) + g_source_remove (port->priv->emit_changed_idle_id); + + /* free properties */ + g_free (port->priv->controller); + g_free (port->priv->parent); + + G_OBJECT_CLASS (devkit_disks_port_parent_class)->finalize (object); +} + +/** + * compute_object_path: + * @port: A #DevkitDisksPort. + * + * Computes the D-Bus object path for the port. + * + * Returns: A valid D-Bus object path. Free with g_free(). + */ +static char * +compute_object_path (DevkitDisksPort *port) +{ + const gchar *basename; + GString *s; + guint n; + + basename = strrchr (port->priv->native_path, '/'); + if (basename != NULL) { + basename++; + } else { + basename = port->priv->native_path; + } + + s = g_string_new (port->priv->parent); + g_string_append_c (s, '/'); + 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 _ */ + g_string_append_printf (s, "_%02x", c); + } + } + + return g_string_free (s, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +register_disks_port (DevkitDisksPort *port) +{ + DBusConnection *connection; + GError *error = NULL; + + port->priv->system_bus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (port->priv->system_bus_connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + goto error; + } + connection = dbus_g_connection_get_connection (port->priv->system_bus_connection); + + port->priv->object_path = compute_object_path (port); + + /* safety first */ + if (dbus_g_connection_lookup_g_object (port->priv->system_bus_connection, + port->priv->object_path) != NULL) { + g_error ("**** HACK: Wanting to register object at path `%s' but there is already an " + "object there. This is an internal error in the daemon. Aborting.\n", + port->priv->object_path); + } + + dbus_g_connection_register_g_object (port->priv->system_bus_connection, + port->priv->object_path, + G_OBJECT (port)); + + return TRUE; + +error: + return FALSE; +} + +void +devkit_disks_port_removed (DevkitDisksPort *port) +{ + port->priv->removed = TRUE; + + dbus_g_connection_unregister_g_object (port->priv->system_bus_connection, + G_OBJECT (port)); + g_assert (dbus_g_connection_lookup_g_object (port->priv->system_bus_connection, + port->priv->object_path) == NULL); +} + +DevkitDisksPort * +devkit_disks_port_new (DevkitDisksDaemon *daemon, GUdevDevice *d) +{ + DevkitDisksPort *port; + const char *native_path; + + port = NULL; + native_path = g_udev_device_get_sysfs_path (d); + + port = DEVKIT_DISKS_PORT (g_object_new (DEVKIT_DISKS_TYPE_PORT, NULL)); + port->priv->d = g_object_ref (d); + port->priv->daemon = g_object_ref (daemon); + port->priv->native_path = g_strdup (native_path); + + if (!update_info (port)) { + g_object_unref (port); + port = NULL; + goto out; + } + + if (!register_disks_port (DEVKIT_DISKS_PORT (port))) { + g_object_unref (port); + port = NULL; + goto out; + } + +out: + return port; +} + +static void +drain_pending_changes (DevkitDisksPort *port, gboolean force_update) +{ + gboolean emit_changed; + + emit_changed = FALSE; + + /* the update-in-idle is set up if, and only if, there are pending changes - so + * we should emit a 'change' event only if it is set up + */ + if (port->priv->emit_changed_idle_id != 0) { + g_source_remove (port->priv->emit_changed_idle_id); + port->priv->emit_changed_idle_id = 0; + emit_changed = TRUE; + } + + if ((!port->priv->removed) && (emit_changed || force_update)) { + if (port->priv->object_path != NULL) { + g_print ("**** EMITTING CHANGED for %s\n", port->priv->native_path); + g_signal_emit_by_name (port, "changed"); + g_signal_emit_by_name (port->priv->daemon, "port-changed", port->priv->object_path); + } + } +} + +/* called by the daemon on the 'change' uevent */ +gboolean +devkit_disks_port_changed (DevkitDisksPort *port, GUdevDevice *d, gboolean synthesized) +{ + gboolean keep_port; + + g_object_unref (port->priv->d); + port->priv->d = g_object_ref (d); + + keep_port = update_info (port); + + /* this 'change' event might prompt us to remove the port */ + if (!keep_port) + goto out; + + /* no, it's good .. keep it.. and always force a 'change' signal if the event isn't synthesized */ + drain_pending_changes (port, !synthesized); + +out: + return keep_port; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +const char * +devkit_disks_port_local_get_object_path (DevkitDisksPort *port) +{ + return port->priv->object_path; +} + +const char * +devkit_disks_port_local_get_native_path (DevkitDisksPort *port) +{ + return port->priv->native_path; +} + +gboolean +devkit_disks_local_port_is_for_device (DevkitDisksPort *port, + const gchar *device_native_path) +{ + return g_str_has_prefix (device_native_path, port->priv->native_path_for_device_prefix); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static char * +sysfs_resolve_link (const char *sysfs_path, const char *name) +{ + char *full_path; + char link_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + ssize_t num; + gboolean found_it; + + found_it = FALSE; + + full_path = g_build_filename (sysfs_path, name, NULL); + + //g_debug ("name='%s'", name); + //g_debug ("full_path='%s'", full_path); + num = readlink (full_path, link_path, sizeof (link_path) - 1); + if (num != -1) { + char *absolute_path; + + link_path[num] = '\0'; + + //g_debug ("link_path='%s'", link_path); + absolute_path = g_build_filename (sysfs_path, link_path, NULL); + //g_debug ("absolute_path='%s'", absolute_path); + if (realpath (absolute_path, resolved_path) != NULL) { + //g_debug ("resolved_path='%s'", resolved_path); + found_it = TRUE; + } + g_free (absolute_path); + } + g_free (full_path); + + if (found_it) + return g_strdup (resolved_path); + else + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gint +int_compare_func (gconstpointer a, + gconstpointer b) +{ + gint a_val; + gint b_val; + + a_val = *((gint *) a); + b_val = *((gint *) b); + + return a_val - b_val; +} + +/* Update info for an ATA port */ +static gboolean +update_info_ata (DevkitDisksPort *port, + DevkitDisksController *controller) +{ + GDir *dir; + GError *error; + gboolean ret; + const gchar *name; + GArray *numbers; + guint n; + const gchar *basename; + gint port_host_number; + gint port_number; + + ret = FALSE; + port_number = -1; + dir = NULL; + numbers = NULL; + + /* First, figure out prefix used for matching the device on the port */ + if (port->priv->native_path_for_device_prefix == NULL) { + port->priv->native_path_for_device_prefix = + sysfs_resolve_link (g_udev_device_get_sysfs_path (port->priv->d), + "device"); + if (port->priv->native_path_for_device_prefix == NULL) { + g_warning ("Unable to resolve 'device' symlink for %s", + g_udev_device_get_sysfs_path (port->priv->d)); + goto out; + } + } + + /* Second, figure out the port number. + * + * As ATA drivers create one scsi_host objects for each port + * the port number can be inferred from the numbering of the + * scsi_host objects. + * + * We also want to check that the scsi_host object *really* + * stems from SATA. There doesn't seem to be an easy way of + * doing this so the heuristic used right now is that it + * the scsi_host is ATA if, and only if, there is more than + * one scsi_host.... + * + * (TODO: we could match on driver names instead? Anyway, + * there are plans to properly support the ATA + * topology (including PMP) once libata switches + * away from SCSI. Once this is available these + * hacks can go away...) + */ + + basename = strrchr (port->priv->native_path, '/'); + if (basename == NULL || sscanf (basename + 1, "host%d", &port_host_number) != 1) { + g_warning ("Cannot extract port host number from %s", port->priv->native_path); + goto out; + } + + dir = g_dir_open (devkit_disks_controller_local_get_native_path (controller), + 0, + &error); + if (dir == NULL) { + g_warning ("Unable to open %s: %s", + devkit_disks_controller_local_get_native_path (controller), + error->message); + g_error_free (error); + goto out; + } + + numbers = g_array_new (FALSE, FALSE, sizeof (gint)); + + while ((name = g_dir_read_name (dir)) != NULL) { + gint number; + + if (sscanf (name, "host%d", &number) != 1) + continue; + + g_array_append_val (numbers, number); + } + + if (numbers->len < 2) { + /* This is (probably, see above) ATA since there isn't at least two + * scsi_host objects. + */ + goto out; + } + + g_array_sort (numbers, int_compare_func); + + for (n = 0; n < numbers->len; n++) { + gint number; + + number = ((gint *) numbers->data) [n]; + + if (number == port_host_number) { + port_number = n; + break; + } + } + + devkit_disks_port_set_number (port, port_number); + ret = TRUE; + + out: + if (dir != NULL) + g_dir_close (dir); + if (numbers != NULL) + g_array_unref (numbers); + + return ret; +} + +/** + * update_info: + * @port: the port + * + * Update information about the port. + * + * If one or more properties changed, the changes are scheduled to be emitted. Use + * drain_pending_changes() to force emitting the pending changes (which is useful + * before returning the result of an operation). + * + * Returns: #TRUE to keep (or add) the port; #FALSE to ignore (or remove) the port + **/ +static gboolean +update_info (DevkitDisksPort *port) +{ + gboolean ret; + DevkitDisksController *controller; + + ret = FALSE; + + controller = devkit_disks_daemon_local_find_controller (port->priv->daemon, + port->priv->native_path); + if (controller == NULL) + goto out; + + /* TODO: handle expansion devices here (SAS expanders, SATA Port Multipliers) */ + devkit_disks_port_set_controller (port, devkit_disks_controller_local_get_object_path (controller)); + devkit_disks_port_set_parent (port, devkit_disks_controller_local_get_object_path (controller)); + + if (g_strcmp0 (g_udev_device_get_subsystem (port->priv->d), "scsi_host") == 0) { + /* TODO: ensure this is really an ATA port - bail if it's not */ + if (!update_info_ata (port, controller)) + goto out; + } else { + goto out; + } + + ret = TRUE; + + out: + return ret; +} + diff --git a/src/devkit-disks-port.h b/src/devkit-disks-port.h new file mode 100644 index 0000000..394154b --- /dev/null +++ b/src/devkit-disks-port.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 David Zeuthen + * + * 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 + * + */ + +#ifndef __DEVKIT_DISKS_PORT_H__ +#define __DEVKIT_DISKS_PORT_H__ + +#include +#include +#include + +#include "devkit-disks-types.h" + +G_BEGIN_DECLS + +#define DEVKIT_DISKS_TYPE_PORT (devkit_disks_port_get_type ()) +#define DEVKIT_DISKS_PORT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPort)) +#define DEVKIT_DISKS_PORT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPortClass)) +#define DEVKIT_DISKS_IS_PORT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DEVKIT_DISKS_TYPE_PORT)) +#define DEVKIT_DISKS_IS_PORT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DEVKIT_DISKS_TYPE_PORT)) +#define DEVKIT_DISKS_PORT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEVKIT_DISKS_TYPE_PORT, DevkitDisksPortClass)) + +typedef struct DevkitDisksPortClass DevkitDisksPortClass; +typedef struct DevkitDisksPortPrivate DevkitDisksPortPrivate; + +struct DevkitDisksPort +{ + GObject parent; + DevkitDisksPortPrivate *priv; +}; + +struct DevkitDisksPortClass +{ + GObjectClass parent_class; +}; + +GType devkit_disks_port_get_type (void) G_GNUC_CONST; + +DevkitDisksPort *devkit_disks_port_new (DevkitDisksDaemon *daemon, + GUdevDevice *d); + +gboolean devkit_disks_port_changed (DevkitDisksPort *port, + GUdevDevice *d, + gboolean synthesized); + +void devkit_disks_port_removed (DevkitDisksPort *port); + +/* local methods */ + +const char *devkit_disks_port_local_get_object_path (DevkitDisksPort *port); +const char *devkit_disks_port_local_get_native_path (DevkitDisksPort *port); +gboolean devkit_disks_local_port_is_for_device (DevkitDisksPort *port, + const gchar *device_native_path); + +G_END_DECLS + +#endif /* __DEVKIT_DISKS_PORT_H__ */ diff --git a/src/devkit-disks-types.h b/src/devkit-disks-types.h index bce8de8..4a7d676 100644 --- a/src/devkit-disks-types.h +++ b/src/devkit-disks-types.h @@ -26,8 +26,11 @@ G_BEGIN_DECLS typedef struct DevkitDisksDaemon DevkitDisksDaemon; + typedef struct DevkitDisksDevice DevkitDisksDevice; typedef struct DevkitDisksController DevkitDisksController; +typedef struct DevkitDisksPort DevkitDisksPort; + typedef struct DevkitDisksMount DevkitDisksMount; typedef struct DevkitDisksMountMonitor DevkitDisksMountMonitor; typedef struct DevkitDisksInhibitor DevkitDisksInhibitor; diff --git a/tools/devkit-disks.c b/tools/devkit-disks.c index 36caba0..abc336f 100644 --- a/tools/devkit-disks.c +++ b/tools/devkit-disks.c @@ -371,6 +371,7 @@ typedef struct guint drive_rotation_rate; char *drive_write_cache; char *drive_controller; + char *drive_port; gboolean optical_disc_is_blank; gboolean optical_disc_is_appendable; @@ -568,6 +569,8 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props) props->drive_write_cache = g_strdup (g_value_get_string (value)); else if (strcmp (key, "DriveController") == 0) props->drive_controller = g_strdup (g_value_get_boxed (value)); + else if (strcmp (key, "DrivePort") == 0) + props->drive_port = g_strdup (g_value_get_boxed (value)); else if (strcmp (key, "OpticalDiscIsBlank") == 0) props->optical_disc_is_blank = g_value_get_boolean (value); @@ -689,6 +692,7 @@ device_properties_free (DeviceProperties *props) g_free (props->drive_media); g_free (props->drive_write_cache); g_free (props->drive_controller); + g_free (props->drive_port); g_free (props->drive_ata_smart_status); g_free (props->drive_ata_smart_blob); @@ -1129,7 +1133,8 @@ do_show_info (const char *object_path) g_print (" write-cache: %s\n", props->drive_write_cache); } g_print (" ejectable: %d\n", props->drive_is_media_ejectable); - g_print (" controller: %s\n", props->drive_controller != NULL ? props->drive_controller : "Unknown"); + g_print (" controller: %s\n", strlen (props->drive_controller) > 1 ? props->drive_controller : "Unknown"); + g_print (" port: %s\n", strlen (props->drive_port) > 1 ? props->drive_port : "Unknown"); g_print (" media: %s\n", props->drive_media); g_print (" compat: "); for (n = 0; props->drive_media_compatibility[n] != NULL; n++)