diff options
author | David Zeuthen <davidz@redhat.com> | 2011-08-07 11:39:58 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2011-08-07 11:39:58 -0400 |
commit | 9a206870bac8d118e8da12ef854aef8d79d7d818 (patch) | |
tree | 4b6cfd186b48c7523b79cccfeb7056b4437e8969 | |
parent | 232ca4d9a33f370bce874dc64e9f407c2c18814d (diff) |
Export SMART data for drives using the ATA protocol
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | data/org.freedesktop.UDisks2.xml | 66 | ||||
-rw-r--r-- | doc/udisks2-docs.xml | 2 | ||||
-rw-r--r-- | doc/udisks2-sections.txt | 61 | ||||
-rw-r--r-- | doc/udisks2.types | 3 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/udiskslinuxdrive.c | 352 | ||||
-rw-r--r-- | src/udiskslinuxdrive.h | 5 | ||||
-rw-r--r-- | src/udiskslinuxprovider.c | 167 | ||||
-rw-r--r-- | udisks/udisksenums.h | 6 | ||||
-rw-r--r-- | udisks/udiskserror.c | 3 |
11 files changed, 647 insertions, 23 deletions
diff --git a/configure.ac b/configure.ac index 04c13c0..0bd86dc 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,10 @@ PKG_CHECK_MODULES(POLKIT_AGENT_1, [polkit-agent-1 >= 0.92]) AC_SUBST(POLKIT_AGENT_1_CFLAGS) AC_SUBST(POLKIT_AGENT_1_LIBS) +PKG_CHECK_MODULES(LIBATASMART, [libatasmart >= 0.17]) +AC_SUBST(LIBATASMART_CFLAGS) +AC_SUBST(LIBATASMART_LIBS) + # Internationalization # diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml index eac12f6..f019b4a 100644 --- a/data/org.freedesktop.UDisks2.xml +++ b/data/org.freedesktop.UDisks2.xml @@ -182,6 +182,72 @@ </method> </interface> + <!-- + org.freedesktop.UDisks2.Drive.Ata: + @short_description: Disk drive using the ATA command-set + + Objects implementing this interface also implement the + #org.freedesktop.UDisks2.Drive interface. + --> + <interface name="org.freedesktop.UDisks2.Drive.Ata"> + <!-- SmartSupported: Whether the drive supports SMART. --> + <property name="SmartSupported" type="b" access="read"/> + + <!-- SmartEnabled: Whether SMART is enabled. --> + <property name="SmartEnabled" type="b" access="read"/> + + <!-- SmartUpdated: + The point in time (seconds since the + <ulink url="http://en.wikipedia.org/wiki/Unix_epoch">Unix Epoch</ulink>) + that the SMART status was updated or 0 if never updated. + --> + <property name="SmartUpdated" type="t" access="read"/> + + <!-- SmartFailing: + Set to %TRUE if disk is about to fail. + + This value is read from the disk itself and does not include + any interpretation. + + This property is only meaningful if the property + #org.freedesktop.UDisks2.Drive.Ata:SmartUpdated is non-zero. + --> + <property name="SmartFailing" type="b" access="read"/> + + <!-- SmartPowerOnSeconds: + The amount of time the disk has been powered on (according to SMART data) or 0 if unknown. + + This property is only meaningful if the property + #org.freedesktop.UDisks2.Drive.Ata:SmartUpdated is non-zero. + --> + <property name="SmartPowerOnSeconds" type="t" access="read"/> + + <!-- SmartTemperature: + The temperature (in Kelvin) of the disk according to SMART data or 0 if unknown. + + This property is only meaningful if the property + #org.freedesktop.UDisks2.Drive.Ata:SmartUpdated is non-zero. + --> + <property name="SmartTemperature" type="d" access="read"/> + + <!-- + SmartUpdate: + @options: Options - known options (in addition to <link linkend="udisks-std-options">standard options</link>) includes <parameter>nowakeup</parameter> (of type 'b'). + + Reads SMART data from the drive and update relevant properties. + + If the option @nowakeup is given and the disk is in a sleeping + state, the error + <literal>org.freedesktop.UDisks.Error.WouldWakeup</literal> is + returned. + --> + <method name="SmartUpdate"> + <arg name="options" direction="in" type="a{sv}"/> + </method> + + <!-- TODO: self-tests, attributes etc. --> + </interface> + <!-- ********************************************************************** --> <!-- diff --git a/doc/udisks2-docs.xml b/doc/udisks2-docs.xml index fc0ca4f..896cbf7 100644 --- a/doc/udisks2-docs.xml +++ b/doc/udisks2-docs.xml @@ -138,6 +138,7 @@ <title>D-Bus Interfaces</title> <xi:include href="../udisks/udisks-generated-doc-org.freedesktop.UDisks2.Manager.xml"/> <xi:include href="../udisks/udisks-generated-doc-org.freedesktop.UDisks2.Drive.xml"/> + <xi:include href="../udisks/udisks-generated-doc-org.freedesktop.UDisks2.Drive.Ata.xml"/> <xi:include href="../udisks/udisks-generated-doc-org.freedesktop.UDisks2.BlockDevice.xml"/> <xi:include href="../udisks/udisks-generated-doc-org.freedesktop.UDisks2.Filesystem.xml"/> <xi:include href="../udisks/udisks-generated-doc-org.freedesktop.UDisks2.Swapspace.xml"/> @@ -158,6 +159,7 @@ <xi:include href="xml/UDisksObjectManagerClient.xml"/> <xi:include href="xml/UDisksManager.xml"/> <xi:include href="xml/UDisksDrive.xml"/> + <xi:include href="xml/UDisksDriveAta.xml"/> <xi:include href="xml/UDisksJob.xml"/> <xi:include href="xml/UDisksBlockDevice.xml"/> <xi:include href="xml/UDisksFilesystem.xml"/> diff --git a/doc/udisks2-sections.txt b/doc/udisks2-sections.txt index 7d766c1..39b2bb1 100644 --- a/doc/udisks2-sections.txt +++ b/doc/udisks2-sections.txt @@ -165,6 +165,7 @@ udisks_linux_drive_new udisks_linux_drive_uevent udisks_linux_drive_get_daemon udisks_linux_drive_get_devices +udisks_linux_drive_housekeeping <SUBSECTION Standard> UDISKS_TYPE_LINUX_DRIVE UDISKS_LINUX_DRIVE @@ -346,6 +347,7 @@ UDisksObject UDisksObjectIface udisks_object_get_block_device udisks_object_get_drive +udisks_object_get_drive_ata udisks_object_get_filesystem udisks_object_get_job udisks_object_get_swapspace @@ -354,6 +356,7 @@ udisks_object_get_loop udisks_object_get_manager udisks_object_peek_block_device udisks_object_peek_drive +udisks_object_peek_drive_ata udisks_object_peek_filesystem udisks_object_peek_job udisks_object_peek_swapspace @@ -368,6 +371,7 @@ UDisksObjectSkeletonClass udisks_object_skeleton_new udisks_object_skeleton_set_block_device udisks_object_skeleton_set_drive +udisks_object_skeleton_set_drive_ata udisks_object_skeleton_set_filesystem udisks_object_skeleton_set_job udisks_object_skeleton_set_swapspace @@ -488,6 +492,63 @@ udisks_drive_skeleton_get_type </SECTION> <SECTION> +<FILE>UDisksDriveAta</FILE> +UDisksDriveAta +UDisksDriveAtaIface +udisks_drive_ata_interface_info +udisks_drive_ata_override_properties +udisks_drive_ata_call_smart_update +udisks_drive_ata_call_smart_update_finish +udisks_drive_ata_call_smart_update_sync +udisks_drive_ata_complete_smart_update +udisks_drive_ata_get_smart_supported +udisks_drive_ata_get_smart_enabled +udisks_drive_ata_get_smart_updated +udisks_drive_ata_get_smart_failing +udisks_drive_ata_get_smart_temperature +udisks_drive_ata_get_smart_power_on_seconds +udisks_drive_ata_set_smart_supported +udisks_drive_ata_set_smart_enabled +udisks_drive_ata_set_smart_updated +udisks_drive_ata_set_smart_failing +udisks_drive_ata_set_smart_temperature +udisks_drive_ata_set_smart_power_on_seconds +UDisksDriveAtaProxy +UDisksDriveAtaProxyClass +udisks_drive_ata_proxy_new +udisks_drive_ata_proxy_new_finish +udisks_drive_ata_proxy_new_sync +udisks_drive_ata_proxy_new_for_bus +udisks_drive_ata_proxy_new_for_bus_finish +udisks_drive_ata_proxy_new_for_bus_sync +UDisksDriveAtaSkeleton +UDisksDriveAtaSkeletonClass +udisks_drive_ata_skeleton_new +<SUBSECTION Standard> +UDISKS_TYPE_DRIVE_ATA +UDISKS_IS_DRIVE_ATA +UDISKS_DRIVE_ATA +UDISKS_DRIVE_ATA_GET_IFACE +UDISKS_TYPE_DRIVE_ATA_PROXY +UDISKS_IS_DRIVE_ATA_PROXY +UDISKS_IS_DRIVE_ATA_PROXY_CLASS +UDISKS_DRIVE_ATA_PROXY +UDISKS_DRIVE_ATA_PROXY_CLASS +UDISKS_DRIVE_ATA_PROXY_GET_CLASS +UDISKS_TYPE_DRIVE_ATA_SKELETON +UDISKS_IS_DRIVE_ATA_SKELETON +UDISKS_IS_DRIVE_ATA_SKELETON_CLASS +UDISKS_DRIVE_ATA_SKELETON +UDISKS_DRIVE_ATA_SKELETON_CLASS +UDISKS_DRIVE_ATA_SKELETON_GET_CLASS +UDisksDriveAtaProxyPrivate +UDisksDriveAtaSkeletonPrivate +udisks_drive_ata_get_type +udisks_drive_ata_proxy_get_type +udisks_drive_ata_skeleton_get_type +</SECTION> + +<SECTION> <FILE>UDisksJob</FILE> UDisksJob UDisksJobIface diff --git a/doc/udisks2.types b/doc/udisks2.types index 2e023c3..7a99f3d 100644 --- a/doc/udisks2.types +++ b/doc/udisks2.types @@ -22,6 +22,9 @@ udisks_cleanup_get_type udisks_drive_get_type udisks_drive_proxy_get_type udisks_drive_skeleton_get_type +udisks_drive_ata_get_type +udisks_drive_ata_proxy_get_type +udisks_drive_ata_skeleton_get_type udisks_block_device_get_type udisks_block_device_proxy_get_type udisks_block_device_skeleton_get_type diff --git a/src/Makefile.am b/src/Makefile.am index 3f0e108..63f0d8b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,7 +97,6 @@ udisksd_SOURCES = \ udisksd_CFLAGS = \ -DG_LOG_DOMAIN=\"udisks2-daemon\" \ - $(LIBATASMART_CFLAGS) \ $(NULL) udisksd_LDADD = \ diff --git a/src/udiskslinuxdrive.c b/src/udiskslinuxdrive.c index 2fded2d..16c54d3 100644 --- a/src/udiskslinuxdrive.c +++ b/src/udiskslinuxdrive.c @@ -25,6 +25,8 @@ #include <stdlib.h> #include <stdio.h> +#include <atasmart.h> + #include "udiskslogging.h" #include "udisksdaemon.h" #include "udisksdaemonutil.h" @@ -59,8 +61,17 @@ struct _UDisksLinuxDrive /* interfaces */ UDisksDrive *iface_drive; + UDisksDriveAta *iface_drive_ata; + + /* ATA Smart */ + guint64 ata_smart_updated; + gboolean ata_smart_failing; + gdouble ata_smart_temperature; + guint64 ata_smart_power_on_seconds; }; +G_LOCK_DEFINE_STATIC (drive_lock); + struct _UDisksLinuxDriveClass { UDisksObjectSkeletonClass parent_class; @@ -81,12 +92,13 @@ 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_list_foreach (drive->devices, (GFunc) g_object_unref, NULL); g_list_free (drive->devices); if (drive->iface_drive != NULL) g_object_unref (drive->iface_drive); + if (drive->iface_drive_ata != NULL) + g_object_unref (drive->iface_drive_ata); if (G_OBJECT_CLASS (udisks_linux_drive_parent_class)->finalize != NULL) G_OBJECT_CLASS (udisks_linux_drive_parent_class)->finalize (object); @@ -584,16 +596,18 @@ drive_check (UDisksLinuxDrive *drive) return TRUE; } +/* TODO: ensure that returned object is for a physical device e.g. not multipath */ static UDisksObject * -find_block_object (GDBusObjectManagerServer *object_manager, - UDisksLinuxDrive *drive) +find_block_object (UDisksLinuxDrive *drive) { + GDBusObjectManagerServer *object_manager; UDisksObject *ret; GList *objects; GList *l; ret = NULL; + object_manager = udisks_daemon_get_object_manager (udisks_linux_drive_get_daemon (drive)); objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager)); for (l = objects; l != NULL; l = l->next) { @@ -646,8 +660,7 @@ on_eject (UDisksDrive *drive_iface, error_message = NULL; daemon = udisks_linux_drive_get_daemon (drive); - block_object = find_block_object (udisks_daemon_get_object_manager (daemon), - drive); + block_object = find_block_object (drive); if (block_object == NULL) { g_dbus_method_invocation_return_error (invocation, @@ -658,8 +671,6 @@ on_eject (UDisksDrive *drive_iface, } 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)) @@ -901,6 +912,261 @@ drive_update (UDisksLinuxDrive *drive, /* ---------------------------------------------------------------------------------------------------- */ +static void drive_ata_smart_update (UDisksLinuxDrive *drive); + +static gboolean +update_smart (UDisksLinuxDrive *drive, + gboolean nowakeup, + GError **error) +{ + gboolean ret; + SkDisk *d; + SkBool awake; + SkBool good; + uint64_t temp_mkelvin; + uint64_t power_on_msec; + GUdevDevice *device; + + d = NULL; + ret = FALSE; + + device = G_UDEV_DEVICE (drive->devices->data); + + if (sk_disk_open (g_udev_device_get_device_file (device), &d) != 0) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "sk_disk_open: %m"); + goto out; + } + + if (sk_disk_check_sleep_mode (d, &awake) != 0) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "sk_disk_check_sleep_mode: %m"); + goto out; + } + + /* don't wake up disk unless specically asked to */ + if (nowakeup && !awake) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_WOULD_WAKEUP, + "Disk is in sleep mode and the nowakeup option was passed"); + goto out; + } + + if (sk_disk_smart_read_data (d) != 0) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "sk_disk_smart_read_data: %m"); + goto out; + } + + if (sk_disk_smart_status (d, &good) != 0) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "sk_disk_smart_status: %m"); + goto out; + } + + /* don't care if these are failing or not */ + temp_mkelvin = 0; + sk_disk_smart_get_temperature (d, &temp_mkelvin); + power_on_msec = 0; + sk_disk_smart_get_power_on (d, &power_on_msec); + + G_LOCK (drive_lock); + drive->ata_smart_updated = time (NULL); + drive->ata_smart_failing = !good; + drive->ata_smart_temperature = temp_mkelvin / 1000.0; + drive->ata_smart_power_on_seconds = power_on_msec / 1000.0; + G_UNLOCK (drive_lock); + + drive_ata_smart_update (drive); + + ret = TRUE; + + out: + if (d != NULL) + sk_disk_free (d); + return ret; +} + +static gboolean +on_smart_update (UDisksDriveAta *drive_ata_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; + gboolean nowakeup; + GError *error; + + daemon = NULL; + block = NULL; + + daemon = udisks_linux_drive_get_daemon (drive); + block_object = find_block_object (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); + + g_variant_lookup (options, + "nowakeup", + "b", + &nowakeup); + + /* 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 update SMART from $(udisks2.device)"), + invocation)) + goto out; + + if (!udisks_drive_ata_get_smart_supported (drive_ata_iface)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "SMART is not supported"); + goto out; + } + + if (!udisks_drive_ata_get_smart_enabled (drive_ata_iface)) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "SMART is not enabled"); + goto out; + } + + error = NULL; + if (!update_smart (drive, nowakeup, &error)) + { + udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)", + g_dbus_object_get_object_path (G_DBUS_OBJECT (drive)), + error->message, g_quark_to_string (error->domain), error->code); + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + udisks_drive_ata_complete_smart_update (drive_ata_iface, invocation); + + out: + if (block_object != NULL) + g_object_unref (block_object); + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +static gboolean +drive_ata_check (UDisksLinuxDrive *drive) +{ + gboolean ret; + GUdevDevice *device; + + ret = FALSE; + if (drive->devices == NULL) + goto out; + + device = G_UDEV_DEVICE (drive->devices->data); + if (!g_udev_device_get_property_as_boolean (device, "ID_ATA")) + goto out; + + ret = TRUE; + + out: + return ret; +} + +static void +drive_ata_connect (UDisksLinuxDrive *drive) +{ + g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (drive->iface_drive_ata), + G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); + g_signal_connect (drive->iface_drive_ata, + "handle-smart-update", + G_CALLBACK (on_smart_update), + drive); +} + +/* also called from *any* thread when the SMART data has been updated */ +static void +drive_ata_smart_update (UDisksLinuxDrive *drive) +{ + GUdevDevice *device; + gboolean supported; + gboolean enabled; + guint64 updated; + gboolean failing; + gdouble temperature; + guint64 power_on_seconds; + + device = G_UDEV_DEVICE (drive->devices->data); + + supported = g_udev_device_get_property_as_boolean (device, "ID_ATA_FEATURE_SET_SMART"); + enabled = g_udev_device_get_property_as_boolean (device, "ID_ATA_FEATURE_SET_SMART_ENABLED"); + updated = 0; + failing = FALSE; + temperature = 0.0; + power_on_seconds = 0; + + G_LOCK (drive_lock); + if (drive->ata_smart_updated > 0) + { + updated = drive->ata_smart_updated; + failing = drive->ata_smart_failing; + temperature = drive->ata_smart_temperature; + power_on_seconds = drive->ata_smart_power_on_seconds; + } + G_UNLOCK (drive_lock); + + g_object_freeze_notify (G_OBJECT (drive->iface_drive_ata)); + udisks_drive_ata_set_smart_supported (drive->iface_drive_ata, supported); + udisks_drive_ata_set_smart_enabled (drive->iface_drive_ata, enabled); + udisks_drive_ata_set_smart_updated (drive->iface_drive_ata, updated); + udisks_drive_ata_set_smart_failing (drive->iface_drive_ata, failing); + udisks_drive_ata_set_smart_temperature (drive->iface_drive_ata, temperature); + udisks_drive_ata_set_smart_power_on_seconds (drive->iface_drive_ata, power_on_seconds); + g_object_thaw_notify (G_OBJECT (drive->iface_drive_ata)); +} + +static void +drive_ata_update (UDisksLinuxDrive *drive, + const gchar *uevent_action, + GDBusInterface *_iface) +{ + drive_ata_smart_update (drive); +} + +/* ---------------------------------------------------------------------------------------------------- */ + static GList * find_link_for_sysfs_path (UDisksLinuxDrive *drive, const gchar *sysfs_path) @@ -968,6 +1234,8 @@ udisks_linux_drive_uevent (UDisksLinuxDrive *drive, update_iface (drive, action, drive_check, drive_connect, drive_update, UDISKS_TYPE_DRIVE_SKELETON, &drive->iface_drive); + update_iface (drive, action, drive_ata_check, drive_ata_connect, drive_ata_update, + UDISKS_TYPE_DRIVE_ATA_SKELETON, &drive->iface_drive_ata); } /* ---------------------------------------------------------------------------------------------------- */ @@ -1037,3 +1305,73 @@ udisks_linux_drive_should_include_device (GUdevDevice *device, g_free (vpd); return ret; } + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * udisks_linux_drive_housekeeping: + * @drive: A #UDisksLinuxDrive. + * @secs_since_last: Number of seconds sincex the last housekeeping or 0 if the first housekeeping ever. + * @cancellable: A %GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Called periodically (every ten minutes or so) to perform + * housekeeping tasks such as refreshing ATA SMART data. + * + * The function runs in a dedicated thread and is allowed to perform + * blocking I/O. + * + * Long-running tasks should periodically check @cancellable to see if + * they have been cancelled. + * + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + */ +gboolean +udisks_linux_drive_housekeeping (UDisksLinuxDrive *drive, + guint secs_since_last, + GCancellable *cancellable, + GError **error) +{ + gboolean ret; + + ret = FALSE; + + if (drive->iface_drive_ata != NULL && + udisks_drive_ata_get_smart_supported (drive->iface_drive_ata) && + udisks_drive_ata_get_smart_enabled (drive->iface_drive_ata)) + { + GError *local_error; + gboolean nowakeup; + + /* Wake-up only on start-up */ + nowakeup = TRUE; + if (secs_since_last == 0) + nowakeup = FALSE; + + udisks_info ("Refreshing SMART data on %s (nowakeup=%d)", + g_dbus_object_get_object_path (G_DBUS_OBJECT (drive)), + nowakeup); + + local_error = NULL; + if (!update_smart (drive, nowakeup, &local_error)) + { + if (nowakeup && (local_error->domain == UDISKS_ERROR && + local_error->code == UDISKS_ERROR_WOULD_WAKEUP)) + { + udisks_info ("Drive %s is in a sleep state", + g_dbus_object_get_object_path (G_DBUS_OBJECT (drive))); + g_error_free (local_error); + } + else + { + g_propagate_prefixed_error (error, local_error, "Error updating SMART data: "); + goto out; + } + } + } + + ret = TRUE; + + out: + return ret; +} diff --git a/src/udiskslinuxdrive.h b/src/udiskslinuxdrive.h index 206696d..f4a1593 100644 --- a/src/udiskslinuxdrive.h +++ b/src/udiskslinuxdrive.h @@ -39,6 +39,11 @@ void udisks_linux_drive_uevent (UDisksLinuxDrive *drive, UDisksDaemon *udisks_linux_drive_get_daemon (UDisksLinuxDrive *drive); GList *udisks_linux_drive_get_devices (UDisksLinuxDrive *drive); +gboolean udisks_linux_drive_housekeeping (UDisksLinuxDrive *drive, + guint secs_since_last, + GCancellable *cancellable, + GError **error); + gboolean udisks_linux_drive_should_include_device (GUdevDevice *device, gchar **out_vpd); diff --git a/src/udiskslinuxprovider.c b/src/udiskslinuxprovider.c index 329bd54..8f29311 100644 --- a/src/udiskslinuxprovider.c +++ b/src/udiskslinuxprovider.c @@ -64,19 +64,26 @@ struct _UDisksLinuxProvider GHashTable *vpd_to_drive; GHashTable *sysfs_path_to_drive; - /* maps from sysfs path to UDisksLinuxController objects */ - GHashTable *sysfs_to_controller; + /* set to TRUE only in the coldplug phase */ + gboolean coldplug; + + guint housekeeping_timeout; + guint64 housekeeping_last; + gboolean housekeeping_running; }; +G_LOCK_DEFINE_STATIC (provider_lock); + struct _UDisksLinuxProviderClass { UDisksProviderClass parent_class; }; -static void -udisks_linux_provider_handle_uevent (UDisksLinuxProvider *provider, - const gchar *action, - GUdevDevice *device); +static void udisks_linux_provider_handle_uevent (UDisksLinuxProvider *provider, + const gchar *action, + GUdevDevice *device); + +static gboolean on_housekeeping_timeout (gpointer user_data); G_DEFINE_TYPE (UDisksLinuxProvider, udisks_linux_provider, UDISKS_TYPE_PROVIDER); @@ -88,12 +95,14 @@ udisks_linux_provider_finalize (GObject *object) g_hash_table_unref (provider->sysfs_to_block); g_hash_table_unref (provider->vpd_to_drive); g_hash_table_unref (provider->sysfs_path_to_drive); - g_hash_table_unref (provider->sysfs_to_controller); g_object_unref (provider->gudev_client); udisks_object_skeleton_set_manager (provider->manager_object, NULL); g_object_unref (provider->manager_object); + if (provider->housekeeping_timeout > 0) + g_source_remove (provider->housekeeping_timeout); + if (G_OBJECT_CLASS (udisks_linux_provider_parent_class)->finalize != NULL) G_OBJECT_CLASS (udisks_linux_provider_parent_class)->finalize (object); } @@ -131,6 +140,8 @@ udisks_linux_provider_start (UDisksProvider *_provider) GList *devices; GList *l; + provider->coldplug = TRUE; + if (UDISKS_PROVIDER_CLASS (udisks_linux_provider_parent_class)->start != NULL) UDISKS_PROVIDER_CLASS (udisks_linux_provider_parent_class)->start (_provider); @@ -156,16 +167,21 @@ udisks_linux_provider_start (UDisksProvider *_provider) g_str_equal, g_free, NULL); - provider->sysfs_to_controller = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) g_object_unref); devices = g_udev_client_query_by_subsystem (provider->gudev_client, "block"); for (l = devices; l != NULL; l = l->next) udisks_linux_provider_handle_uevent (provider, "add", G_UDEV_DEVICE (l->data)); g_list_foreach (devices, (GFunc) g_object_unref, NULL); g_list_free (devices); + + /* schedule housekeeping for every 10 minutes */ + provider->housekeeping_timeout = g_timeout_add_seconds (10*60, + on_housekeeping_timeout, + provider); + /* ... and also do an initial run */ + on_housekeeping_timeout (provider); + + provider->coldplug = FALSE; } @@ -216,6 +232,28 @@ udisks_linux_provider_get_udev_client (UDisksLinuxProvider *provider) /* ---------------------------------------------------------------------------------------------------- */ +static gboolean +perform_initial_housekeeping_for_drive (GIOSchedulerJob *job, + GCancellable *cancellable, + gpointer user_data) +{ + UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (user_data); + GError *error; + + error = NULL; + if (!udisks_linux_drive_housekeeping (drive, 0, + NULL, /* TODO: cancellable */ + &error)) + { + udisks_warning ("Error performing initial housekeeping for drive %s: %s (%s, %d)", + g_dbus_object_get_object_path (G_DBUS_OBJECT (drive)), + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + } + return FALSE; /* job is complete */ +} + +/* called with lock held */ static void handle_block_uevent_for_drive (UDisksLinuxProvider *provider, const gchar *action, @@ -282,6 +320,16 @@ handle_block_uevent_for_drive (UDisksLinuxProvider *provider, G_DBUS_OBJECT_SKELETON (drive)); g_hash_table_insert (provider->vpd_to_drive, g_strdup (vpd), drive); g_hash_table_insert (provider->sysfs_path_to_drive, g_strdup (sysfs_path), drive); + + /* schedule initial housekeeping for the drive unless coldplugging */ + if (!provider->coldplug) + { + g_io_scheduler_push_job (perform_initial_housekeeping_for_drive, + g_object_ref (drive), + (GDestroyNotify) g_object_unref, + G_PRIORITY_DEFAULT, + NULL); + } } } } @@ -290,6 +338,7 @@ handle_block_uevent_for_drive (UDisksLinuxProvider *provider, g_free (vpd); } +/* called with lock held */ static void handle_block_uevent_for_block (UDisksLinuxProvider *provider, const gchar *action, @@ -329,7 +378,7 @@ handle_block_uevent_for_block (UDisksLinuxProvider *provider, } } - +/* called with lock held */ static void handle_block_uevent (UDisksLinuxProvider *provider, const gchar *action, @@ -357,6 +406,7 @@ handle_block_uevent (UDisksLinuxProvider *provider, } } +/* called without lock held */ static void udisks_linux_provider_handle_uevent (UDisksLinuxProvider *provider, const gchar *action, @@ -364,6 +414,8 @@ udisks_linux_provider_handle_uevent (UDisksLinuxProvider *provider, { const gchar *subsystem; + G_LOCK (provider_lock); + udisks_debug ("uevent %s %s", action, g_udev_device_get_sysfs_path (device)); @@ -373,4 +425,95 @@ udisks_linux_provider_handle_uevent (UDisksLinuxProvider *provider, { handle_block_uevent (provider, action, device); } + + G_UNLOCK (provider_lock); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* Runs in housekeeping thread - called without lock held */ +static void +housekeeping_all_drives (UDisksLinuxProvider *provider, + guint secs_since_last) +{ + GList *drives; + GList *l; + + G_LOCK (provider_lock); + drives = g_hash_table_get_values (provider->vpd_to_drive); + g_list_foreach (drives, (GFunc) g_object_ref, NULL); + G_UNLOCK (provider_lock); + + for (l = drives; l != NULL; l = l->next) + { + UDisksLinuxDrive *drive = UDISKS_LINUX_DRIVE (l->data); + GError *error; + + error = NULL; + if (!udisks_linux_drive_housekeeping (drive, + secs_since_last, + NULL, /* TODO: cancellable */ + &error)) + { + udisks_warning ("Error performing housekeeping for drive %s: %s (%s, %d)", + g_dbus_object_get_object_path (G_DBUS_OBJECT (drive)), + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + } + } + + g_list_foreach (drives, (GFunc) g_object_unref, NULL); + g_list_free (drives); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +housekeeping_thread_func (GIOSchedulerJob *job, + GCancellable *cancellable, + gpointer user_data) +{ + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); + guint secs_since_last; + guint64 now; + + /* TODO: probably want some kind of timeout here to avoid faulty devices/drives blocking forever */ + + secs_since_last = 0; + now = time (NULL); + if (provider->housekeeping_last > 0) + secs_since_last = now - provider->housekeeping_last; + provider->housekeeping_last = now; + + udisks_info ("Housekeeping initiated (%d seconds since last housekeeping)", secs_since_last); + + housekeeping_all_drives (provider, secs_since_last); + + udisks_info ("Housekeeping complete"); + G_LOCK (provider_lock); + provider->housekeeping_running = FALSE; + G_UNLOCK (provider_lock); + + return FALSE; /* job is complete */ +} + +/* called from the main thread on start-up and every 10 minutes or so */ +static gboolean +on_housekeeping_timeout (gpointer user_data) +{ + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); + + G_LOCK (provider_lock); + if (provider->housekeeping_running) + goto out; + provider->housekeeping_running = TRUE; + g_io_scheduler_push_job (housekeeping_thread_func, + g_object_ref (provider), + (GDestroyNotify) g_object_unref, + G_PRIORITY_DEFAULT, + NULL); + out: + G_UNLOCK (provider_lock); + + return TRUE; /* keep timeout around */ } diff --git a/udisks/udisksenums.h b/udisks/udisksenums.h index 4d02129..d9afe60 100644 --- a/udisks/udisksenums.h +++ b/udisks/udisksenums.h @@ -44,6 +44,7 @@ G_BEGIN_DECLS * @UDISKS_ERROR_ALREADY_UNMOUNTING: The device is already unmounting. * @UDISKS_ERROR_NOT_SUPPORTED: The operation is not supported due to missing driver/tool support. * @UDISKS_ERROR_TIMED_OUT: The operation timed out. + * @UDISKS_ERROR_WOULD_WAKEUP: The operation would wake up a disk that is in a deep-sleep state. * * Error codes for the #UDISKS_ERROR error domain and the * corresponding D-Bus error names. @@ -62,10 +63,11 @@ typedef enum UDISKS_ERROR_MOUNTED_BY_OTHER_USER, /* org.freedesktop.UDisks.Error.MountedByOtherUser */ UDISKS_ERROR_ALREADY_UNMOUNTING, /* org.freedesktop.UDisks.Error.AlreadyUnmounting */ UDISKS_ERROR_NOT_SUPPORTED, /* org.freedesktop.UDisks.Error.NotSupported */ - UDISKS_ERROR_TIMED_OUT /* org.freedesktop.UDisks.Error.Timedout */ + UDISKS_ERROR_TIMED_OUT, /* org.freedesktop.UDisks.Error.Timedout */ + UDISKS_ERROR_WOULD_WAKEUP, /* org.freedesktop.UDisks.Error.WouldWakeup */ } UDisksError; -#define UDISKS_ERROR_NUM_ENTRIES (UDISKS_ERROR_TIMED_OUT + 1) +#define UDISKS_ERROR_NUM_ENTRIES (UDISKS_ERROR_WOULD_WAKEUP + 1) G_END_DECLS diff --git a/udisks/udiskserror.c b/udisks/udiskserror.c index 70cbb12..27e81bd 100644 --- a/udisks/udiskserror.c +++ b/udisks/udiskserror.c @@ -45,7 +45,8 @@ static const GDBusErrorEntry dbus_error_entries[] = {UDISKS_ERROR_MOUNTED_BY_OTHER_USER, "org.freedesktop.UDisks.Error.MountedByOtherUser"}, {UDISKS_ERROR_ALREADY_UNMOUNTING, "org.freedesktop.UDisks.Error.AlreadyUnmounting"}, {UDISKS_ERROR_NOT_SUPPORTED, "org.freedesktop.UDisks.Error.NotSupported"}, - {UDISKS_ERROR_TIMED_OUT, "org.freedesktop.UDisks.Error.Timedout"} + {UDISKS_ERROR_TIMED_OUT, "org.freedesktop.UDisks.Error.Timedout"}, + {UDISKS_ERROR_WOULD_WAKEUP, "org.freedesktop.UDisks.Error.WouldWakeup"}, }; GQuark |