summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2011-08-07 11:39:58 -0400
committerDavid Zeuthen <davidz@redhat.com>2011-08-07 11:39:58 -0400
commit9a206870bac8d118e8da12ef854aef8d79d7d818 (patch)
tree4b6cfd186b48c7523b79cccfeb7056b4437e8969
parent232ca4d9a33f370bce874dc64e9f407c2c18814d (diff)
Export SMART data for drives using the ATA protocol
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r--configure.ac4
-rw-r--r--data/org.freedesktop.UDisks2.xml66
-rw-r--r--doc/udisks2-docs.xml2
-rw-r--r--doc/udisks2-sections.txt61
-rw-r--r--doc/udisks2.types3
-rw-r--r--src/Makefile.am1
-rw-r--r--src/udiskslinuxdrive.c352
-rw-r--r--src/udiskslinuxdrive.h5
-rw-r--r--src/udiskslinuxprovider.c167
-rw-r--r--udisks/udisksenums.h6
-rw-r--r--udisks/udiskserror.c3
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