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.
+
+ The value of the other properties related to SMART are not
+ meaningful if this proeprty is 0.
-->
<property name="SmartUpdated" type="t" access="read"/>
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.
+ -->
+ <property name="SmartTemperature" type="d" access="read"/>
+
+ <!--
+ SmartNumAttributesFailing:
+ The number of attributes failing right now or -1 if unknown.
+ -->
+ <property name="SmartNumAttributesFailing" type="i" access="read"/>
+ <!--
+ SmartNumAttributesFailedInThePast:
+ The number of attributes that have failed in the past or -1 if unknown.
+ -->
+ <property name="SmartNumAttributesFailedInThePast" type="i" access="read"/>
+
+ <!--
+ SmartNumBadSectors:
+ The number of bad sectors (ie. pending and reallocated) or -1 if unknown.
+ -->
+ <property name="SmartNumBadSectors" type="x" access="read"/>
+
+ <!-- SmartSelftestStatus:
+ The status of the last self-test. Known values include
+ <variablelist>
+ <varlistentry><term>success</term>
+ <listitem><para>Last self-test was a success (or never ran).</para></listitem></varlistentry>
+ <varlistentry><term>aborted</term>
+ <listitem><para>Last self-test was aborted.</para></listitem></varlistentry>
+ <varlistentry><term>interrupted</term>
+ <listitem><para>Last self-test was interrupted.</para></listitem></varlistentry>
+ <varlistentry><term>fatal</term>
+ <listitem><para>Last self-test did not complete.</para></listitem></varlistentry>
+ <varlistentry><term>error_unknown</term>
+ <listitem><para>Last self-test failed (Unknown).</para></listitem></varlistentry>
+ <varlistentry><term>error_electrical</term>
+ <listitem><para>Last self-test failed (Electrical).</para></listitem></varlistentry>
+ <varlistentry><term>error_servo</term>
+ <listitem><para>Last self-test failed (Servo).</para></listitem></varlistentry>
+ <varlistentry><term>error_read</term>
+ <listitem><para>Last self-test failed (Read).</para></listitem></varlistentry>
+ <varlistentry><term>error_handling</term>
+ <listitem><para>Last self-test failed (Damage)).</para></listitem></varlistentry>
+ <varlistentry><term>inprogress</term>
+ <listitem><para>Self-test is currently in progress.</para></listitem></varlistentry>
+ </variablelist>
This property is only meaningful if the property
#org.freedesktop.UDisks2.Drive.Ata:SmartUpdated is non-zero.
-->
- <property name="SmartTemperature" type="d" access="read"/>
+ <property name="SmartSelftestStatus" type="s" access="read"/>
+
+ <!--
+ SmartSelftestPercentRemaining:
+ The percent remaining or -1 if unknown.
+ -->
+ <property name="SmartSelftestPercentRemaining" type="i" access="read"/>
<!--
SmartUpdate:
<arg name="options" direction="in" type="a{sv}"/>
</method>
- <!-- TODO: self-tests, attributes etc. -->
+ <!--
+ SmartGetAttributes:
+ @options: Options - known options (in addition to <link linkend="udisks-std-options">standard options</link>) includes <parameter>nowakeup</parameter> (of type 'b').
+ @attributes: The SMART attributes.
+
+ Get the SMART attributes.
+ Each attribute is a struct with the following members:
+ <variablelist>
+ <varlistentry><term>id (type 'y')</term>
+ <listitem><para>Attribute Identifier</para></listitem></varlistentry>
+ <varlistentry><term>name (type 's')</term>
+ <listitem><para>The identifier as a string.</para></listitem></varlistentry>
+ <varlistentry><term>flags (type 'q')</term>
+ <listitem><para>16-bit attribute flags (bit 0 is prefail/oldage, bit 1 is online/offline).</para></listitem></varlistentry>
+ <varlistentry><term>value (type 'i')</term>
+ <listitem><para>The current value or -1 if unknown.</para></listitem></varlistentry>
+ <varlistentry><term>worst (type 'i')</term>
+ <listitem><para>The worst value of -1 if unknown.</para></listitem></varlistentry>
+ <varlistentry><term>threshold (type 'i')</term>
+ <listitem><para>The threshold or -1 if unknown.</para></listitem></varlistentry>
+ <varlistentry><term>pretty (type 'x')</term>
+ <listitem><para>An interpretation of the value - must be ignored if @pretty_unit is 0.</para></listitem></varlistentry>
+ <varlistentry><term>pretty_unit (type 'i')</term>
+ <listitem><para>The unit of the @pretty value - the following units are known: 0 (unknown), 1 (dimensionless), 2 (milliseconds), 3 (sectors), 4 (millikelvin).</para></listitem></varlistentry>
+ <varlistentry><term>expansion (type 'a{sv}')</term>
+ <listitem><para>Currently unused. Intended for future expansion.</para></listitem></varlistentry>
+ </variablelist>
+ The @name parameter should be used as the authoritative identifier for the attribute since it is derived from the numerical @id and the disk's <literal>IDENTIFY</literal> data and thus handles ID collisions between drives of different make and model.
+ -->
+ <method name="SmartGetAttributes">
+ <arg name="options" direction="in" type="a{sv}"/>
+ <arg name="attributes" direction="out" type="a(ysqiiixia{sv})"/>
+ </method>
+
+ <!--
+ SmartSelftestStart:
+ @type: The type test to run.
+ @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
+
+ Starts a SMART selftest. The @type parameter is for the type
+ of test to start - valid values are <literal>short</literal>,
+ <literal>extended</literal> and <literal>conveyance</literal>.
+
+ Note that the method returns immediately after the test has
+ been started successfully.
+ -->
+ <method name="SmartSelftestStart">
+ <arg name="type" direction="in" type="s"/>
+ <arg name="options" direction="in" type="a{sv}"/>
+ </method>
+
+ <!--
+ SmartSelftestAbort:
+ @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>).
+
+ Aborts a running SMART selftest.
+ -->
+ <method name="SmartSelftestAbort">
+ <arg name="options" direction="in" type="a{sv}"/>
+ </method>
+
</interface>
<!-- ********************************************************************** -->
#include <mntent.h>
#include <glib/gstdio.h>
+#include <errno.h>
#include <atasmart.h>
#include "udisksdaemon.h"
#include "udiskscleanup.h"
#include "udisksdaemonutil.h"
+#include "udisksthreadedjob.h"
/**
* SECTION:udiskslinuxdriveata
{
UDisksDriveAtaSkeleton parent_instance;
- guint64 smart_updated;
- gboolean smart_failing;
- gdouble smart_temperature;
- guint64 smart_power_on_seconds;
+ guint64 smart_updated;
+ gboolean smart_failing;
+ gdouble smart_temperature;
+ guint64 smart_power_on_seconds;
+ gint smart_num_attributes_failing;
+ gint smart_num_attributes_failed_in_the_past;
+ gint64 smart_num_bad_sectors;
+ const gchar *smart_selftest_status;
+ gint smart_selftest_percent_remaining;
+
+ GVariant *smart_attributes;
+
+ UDisksThreadedJob *selftest_job;
+ GCancellable *selftest_cancellable;
};
struct _UDisksLinuxDriveAtaClass
/* ---------------------------------------------------------------------------------------------------- */
static void
-udisks_linux_drive_ata_init (UDisksLinuxDriveAta *drive_ata)
+udisks_linux_drive_ata_finalize (GObject *object)
+{
+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (object);
+
+ if (drive->smart_attributes != NULL)
+ g_variant_unref (drive->smart_attributes);
+
+ if (G_OBJECT_CLASS (udisks_linux_drive_ata_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (udisks_linux_drive_ata_parent_class)->finalize (object);
+}
+
+
+static void
+udisks_linux_drive_ata_init (UDisksLinuxDriveAta *drive)
{
- g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (drive_ata),
+ g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (drive),
G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
}
static void
udisks_linux_drive_ata_class_init (UDisksLinuxDriveAtaClass *klass)
{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = udisks_linux_drive_ata_finalize;
}
/**
update_smart (UDisksLinuxDriveAta *drive,
GUdevDevice *device)
{
- gboolean supported;
- gboolean enabled;
- guint64 updated;
- gboolean failing;
- gdouble temperature;
- guint64 power_on_seconds;
+ gboolean supported = FALSE;
+ gboolean enabled = FALSE;
+ guint64 updated = 0;
+ gboolean failing = FALSE;
+ gdouble temperature = 0.0;
+ guint64 power_on_seconds = 0;
+ const gchar *selftest_status = NULL;
+ gint selftest_percent_remaining = -1;
+ gint num_attributes_failing = -1;
+ gint num_attributes_failed_in_the_past = -1;
+ gint64 num_bad_sectors = 1;
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 (object_lock);
if (drive->smart_updated > 0)
failing = drive->smart_failing;
temperature = drive->smart_temperature;
power_on_seconds = drive->smart_power_on_seconds;
+ num_attributes_failing = drive->smart_num_attributes_failing;
+ num_attributes_failed_in_the_past = drive->smart_num_attributes_failed_in_the_past;
+ num_bad_sectors = drive->smart_num_bad_sectors;
+ selftest_status = drive->smart_selftest_status;
+ selftest_percent_remaining = drive->smart_selftest_percent_remaining;
}
G_UNLOCK (object_lock);
+ if (selftest_status == NULL)
+ selftest_status = "";
+
g_object_freeze_notify (G_OBJECT (drive));
udisks_drive_ata_set_smart_supported (UDISKS_DRIVE_ATA (drive), supported);
udisks_drive_ata_set_smart_enabled (UDISKS_DRIVE_ATA (drive), enabled);
udisks_drive_ata_set_smart_failing (UDISKS_DRIVE_ATA (drive), failing);
udisks_drive_ata_set_smart_temperature (UDISKS_DRIVE_ATA (drive), temperature);
udisks_drive_ata_set_smart_power_on_seconds (UDISKS_DRIVE_ATA (drive), power_on_seconds);
+ udisks_drive_ata_set_smart_num_attributes_failing (UDISKS_DRIVE_ATA (drive), num_attributes_failing);
+ udisks_drive_ata_set_smart_num_attributes_failed_in_the_past (UDISKS_DRIVE_ATA (drive), num_attributes_failed_in_the_past);
+ udisks_drive_ata_set_smart_num_bad_sectors (UDISKS_DRIVE_ATA (drive), num_bad_sectors);
+ udisks_drive_ata_set_smart_selftest_status (UDISKS_DRIVE_ATA (drive), selftest_status);
+ udisks_drive_ata_set_smart_selftest_percent_remaining (UDISKS_DRIVE_ATA (drive), selftest_percent_remaining);
g_object_thaw_notify (G_OBJECT (drive));
}
/* ---------------------------------------------------------------------------------------------------- */
+typedef struct
+{
+ GVariantBuilder builder;
+ gint num_attributes_failing;
+ gint num_attributes_failed_in_the_past;
+} ParseData;
+
+static void
+parse_attr_cb (SkDisk *d,
+ const SkSmartAttributeParsedData *a,
+ void *user_data)
+{
+ ParseData *data = user_data;
+ gboolean failed = FALSE;
+ gboolean failed_in_the_past = FALSE;
+ gint current, worst, threshold;
+
+ current = a->current_value_valid ? a->current_value : -1;
+ worst = a->worst_value_valid ? a->worst_value : -1;
+ threshold = a->threshold_valid ? a->threshold : -1;
+
+ g_variant_builder_add (&data->builder,
+ "(ysqiiixia{sv})",
+ a->id,
+ a->name,
+ a->flags,
+ current,
+ worst,
+ threshold,
+ a->pretty_value, a->pretty_unit,
+ NULL); /* expansion unused for now */
+
+ if (current > 0 && threshold > 0 && current <= threshold)
+ failed = TRUE;
+
+ if (worst > 0 && threshold > 0 && worst <= threshold)
+ failed_in_the_past = TRUE;
+
+ if (failed)
+ data->num_attributes_failing += 1;
+
+ if (failed_in_the_past)
+ data->num_attributes_failed_in_the_past += 1;
+}
+
+static const gchar *
+selftest_status_to_string (SkSmartSelfTestExecutionStatus status)
+{
+ const gchar *ret;
+ switch (status)
+ {
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER:
+ ret = "success";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED:
+ ret = "aborted";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED:
+ ret = "interrupted";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL:
+ ret = "fatal";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN:
+ ret = "error_unknown";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL:
+ ret = "error_electrical";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO:
+ ret = "error_servo";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ:
+ ret = "error_read";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING:
+ ret = "error_handling";
+ break;
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS:
+ ret = "inprogress";
+ break;
+ default:
+ ret = "";
+ break;
+ }
+ return ret;
+}
+
/**
* udisks_linux_drive_ata_refresh_smart_sync:
* @drive: The #UDisksLinuxDriveAta to refresh.
* @nowakeup: If %TRUE, will not wake up the disk if asleep.
+ * @simulate_path: If not %NULL, the path of a file with a libatasmart blob to use.
* @cancellable: A #GCancellable or %NULL.
* @error: Return location for error.
*
gboolean
udisks_linux_drive_ata_refresh_smart_sync (UDisksLinuxDriveAta *drive,
gboolean nowakeup,
+ const gchar *simulate_path,
GCancellable *cancellable,
GError **error)
{
SkDisk *d;
SkBool awake;
SkBool good;
- uint64_t temp_mkelvin;
- uint64_t power_on_msec;
+ uint64_t temp_mkelvin = 0;
+ uint64_t power_on_msec = 0;
+ uint64_t num_bad_sectors = 0;
+ const SkSmartParsedData *data;
+ ParseData parse_data;
object = UDISKS_LINUX_DRIVE_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (drive)));
device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
d = NULL;
ret = FALSE;
- if (sk_disk_open (g_udev_device_get_device_file (device), &d) != 0)
+ if (simulate_path != NULL)
{
- g_set_error (error,
- UDISKS_ERROR,
- UDISKS_ERROR_FAILED,
- "sk_disk_open: %m");
- goto out;
+ gchar *blob;
+ gsize blob_len;
+
+ if (!g_file_get_contents (simulate_path,
+ &blob,
+ &blob_len,
+ error))
+ {
+ goto out;
+ }
+
+ if (sk_disk_open (NULL, &d) != 0)
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "sk_disk_open: %m");
+ goto out;
+ }
+
+ if (sk_disk_set_blob (d, blob, blob_len) != 0)
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "sk_disk_set_blob: %m");
+ g_free (blob);
+ goto out;
+ }
+ g_free (blob);
}
-
- if (sk_disk_check_sleep_mode (d, &awake) != 0)
+ else
{
- g_set_error (error,
- UDISKS_ERROR,
- UDISKS_ERROR_FAILED,
- "sk_disk_check_sleep_mode: %m");
- goto out;
+ 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;
+ }
}
- /* don't wake up disk unless specically asked to */
- if (nowakeup && !awake)
+ if (sk_disk_smart_read_data (d) != 0)
{
g_set_error (error,
UDISKS_ERROR,
- UDISKS_ERROR_WOULD_WAKEUP,
- "Disk is in sleep mode and the nowakeup option was passed");
+ UDISKS_ERROR_FAILED,
+ "sk_disk_smart_read_data: %m");
goto out;
}
- if (sk_disk_smart_read_data (d) != 0)
+ if (sk_disk_smart_status (d, &good) != 0)
{
g_set_error (error,
UDISKS_ERROR,
UDISKS_ERROR_FAILED,
- "sk_disk_smart_read_data: %m");
+ "sk_disk_smart_status: %m");
goto out;
}
- if (sk_disk_smart_status (d, &good) != 0)
+ if (sk_disk_smart_parse (d, &data) != 0)
{
g_set_error (error,
UDISKS_ERROR,
UDISKS_ERROR_FAILED,
- "sk_disk_smart_status: %m");
+ "sk_disk_smart_parse: %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);
+ sk_disk_smart_get_bad (d, &num_bad_sectors);
+
+ memset (&parse_data, 0, sizeof (ParseData));
+ g_variant_builder_init (&parse_data.builder, G_VARIANT_TYPE ("a(ysqiiixia{sv})"));
+ sk_disk_smart_parse_attributes (d, parse_attr_cb, &parse_data);
G_LOCK (object_lock);
drive->smart_updated = time (NULL);
drive->smart_failing = !good;
drive->smart_temperature = temp_mkelvin / 1000.0;
drive->smart_power_on_seconds = power_on_msec / 1000.0;
+ drive->smart_num_attributes_failing = parse_data.num_attributes_failing;
+ drive->smart_num_attributes_failed_in_the_past = parse_data.num_attributes_failed_in_the_past;
+ drive->smart_num_bad_sectors = num_bad_sectors;
+ drive->smart_selftest_status = selftest_status_to_string (data->self_test_execution_status);
+ drive->smart_selftest_percent_remaining = data->self_test_execution_percent_remaining;
+ if (drive->smart_attributes != NULL)
+ g_variant_unref (drive->smart_attributes);
+ drive->smart_attributes = g_variant_ref_sink (g_variant_builder_end (&parse_data.builder));
G_UNLOCK (object_lock);
update_smart (drive, device);
/* ---------------------------------------------------------------------------------------------------- */
+gboolean
+udisks_linux_drive_ata_smart_selftest_sync (UDisksLinuxDriveAta *drive,
+ const gchar *type,
+ GCancellable *cancellable,
+ GError **error)
+{
+ UDisksLinuxDriveObject *object;
+ GUdevDevice *device;
+ SkDisk *d = NULL;
+ gboolean ret = FALSE;
+ SkSmartSelfTest test;
+
+ object = UDISKS_LINUX_DRIVE_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (drive)));
+ device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
+ g_assert (device != NULL);
+
+ if (g_strcmp0 (type, "short") == 0)
+ test = SK_SMART_SELF_TEST_SHORT;
+ else if (g_strcmp0 (type, "extended") == 0)
+ test = SK_SMART_SELF_TEST_EXTENDED;
+ else if (g_strcmp0 (type, "conveyance") == 0)
+ test = SK_SMART_SELF_TEST_CONVEYANCE;
+ else if (g_strcmp0 (type, "abort") == 0)
+ test = SK_SMART_SELF_TEST_ABORT;
+ else
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "unknown type %s", type);
+ goto out;
+ }
+
+ 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_smart_self_test (d, test) != 0)
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "sk_disk_smart_self_test: %m");
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ g_object_unref (device);
+ if (d != NULL)
+ sk_disk_free (d);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static gboolean
handle_smart_update (UDisksDriveAta *_drive,
GDBusMethodInvocation *invocation,
UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
UDisksLinuxDriveObject *object;
UDisksLinuxBlockObject *block_object;
- UDisksBlock *block;
UDisksDaemon *daemon;
- const gchar *action_id;
- gboolean nowakeup;
+ gboolean nowakeup = FALSE;
+ const gchar *atasmart_blob = NULL;
GError *error;
daemon = NULL;
- block = NULL;
object = UDISKS_LINUX_DRIVE_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (drive)));
daemon = udisks_linux_drive_object_get_daemon (object);
"Unable to find physical block device for drive");
goto out;
}
- block = udisks_object_peek_block (UDISKS_OBJECT (block_object));
- g_variant_lookup (options,
- "nowakeup",
- "b",
- &nowakeup);
+ g_variant_lookup (options, "nowakeup", "b", &nowakeup);
+ g_variant_lookup (options, "atasmart-blob", "s", &atasmart_blob);
- /* TODO: is it a good idea to overload modify-device? */
- action_id = "org.freedesktop.udisks2.modify-device";
- if (udisks_block_get_hint_system (block))
- action_id = "org.freedesktop.udisks2.modify-device-system";
+ if (atasmart_blob != NULL)
+ {
+ uid_t caller_uid;
+ error = NULL;
+ if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL /* GCancellable */, &caller_uid, &error))
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ g_error_free (error);
+ goto out;
+ }
+ if (caller_uid != 0)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Only root can update SMART data from a blob");
+ goto out;
+ }
+ }
+ else
+ {
+ /* Check that the user is actually authorized */
+ if (!udisks_daemon_util_check_authorization_sync (daemon,
+ UDISKS_OBJECT (block_object),
+ "org.freedesktop.udisks2.ata-smart-update",
+ options,
+ N_("Authentication is required to update SMART data from $(udisks2.device)"),
+ invocation))
+ goto out;
+
+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)))
+ {
+ 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 (UDISKS_DRIVE_ATA (drive)))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "SMART is not enabled");
+ goto out;
+ }
+ }
+
+ error = NULL;
+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
+ nowakeup,
+ atasmart_blob,
+ NULL, /* cancellable */
+ &error))
+ {
+ udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
+ 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 (UDISKS_DRIVE_ATA (drive), invocation);
+
+ out:
+ if (block_object != NULL)
+ g_object_unref (block_object);
+ return TRUE; /* returning TRUE means that we handled the method invocation */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+handle_smart_get_attributes (UDisksDriveAta *_drive,
+ GDBusMethodInvocation *invocation,
+ GVariant *options)
+{
+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
+
+ G_LOCK (object_lock);
+ if (drive->smart_attributes == NULL)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "SMART data not collected");
+ }
+ else
+ {
+ udisks_drive_ata_complete_smart_get_attributes (UDISKS_DRIVE_ATA (drive), invocation,
+ drive->smart_attributes);
+ }
+ G_UNLOCK (object_lock);
+
+ return TRUE; /* returning TRUE means that we handled the method invocation */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+handle_smart_selftest_abort (UDisksDriveAta *_drive,
+ GDBusMethodInvocation *invocation,
+ GVariant *options)
+{
+ UDisksLinuxDriveObject *object;
+ UDisksLinuxBlockObject *block_object;
+ UDisksDaemon *daemon;
+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
+ GError *error;
+
+ object = UDISKS_LINUX_DRIVE_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (drive)));
+ daemon = udisks_linux_drive_object_get_daemon (object);
+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
+ 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;
+ }
+
+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)) ||
+ !udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "SMART is not supported or enabled");
+ goto out;
+ }
- /* Check that the user is actually authorized */
if (!udisks_daemon_util_check_authorization_sync (daemon,
UDISKS_OBJECT (block_object),
- action_id,
+ "org.freedesktop.udisks2.ata-smart-selftest",
options,
- N_("Authentication is required to update S.M.A.R.T. data from $(udisks2.device)"),
+ N_("Authentication is required to abort a SMART self-test on $(udisks2.device)"),
invocation))
goto out;
- if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)))
+ error = NULL;
+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
+ "abort",
+ NULL, /* cancellable */
+ &error))
+ {
+ udisks_warning ("Error aborting SMART selftest for %s: %s (%s, %d)",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
+ error->message, g_quark_to_string (error->domain), error->code);
+ g_dbus_method_invocation_take_error (invocation, error);
+ goto out;
+ }
+
+ /* TODO: wakeup selftest thread and wait for it to terminate */
+ G_LOCK (object_lock);
+ if (drive->selftest_cancellable != NULL)
+ {
+ g_cancellable_cancel (drive->selftest_cancellable);
+ }
+ G_UNLOCK (object_lock);
+
+ error = NULL;
+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
+ FALSE, /* nowakeup */
+ NULL, /* blob */
+ NULL, /* cancellable */
+ &error))
+ {
+ udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
+ 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_selftest_abort (UDISKS_DRIVE_ATA (drive), invocation);
+
+ out:
+ return TRUE; /* returning TRUE means that we handled the method invocation */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+selftest_job_func (UDisksThreadedJob *job,
+ GCancellable *cancellable,
+ gpointer user_data,
+ GError **error)
+{
+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (user_data);
+ UDisksLinuxDriveObject *object;
+ gboolean ret = FALSE;
+
+ object = UDISKS_LINUX_DRIVE_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (drive)));
+
+ while (TRUE)
+ {
+ gboolean still_in_progress;
+ GPollFD poll_fd;
+
+ if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
+ FALSE, /* nowakeup */
+ NULL, /* blob */
+ NULL, /* cancellable */
+ error))
+ {
+ udisks_warning ("Error updating ATA smart for %s while polling during self-test: %s (%s, %d)",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
+ (*error)->message, g_quark_to_string ((*error)->domain), (*error)->code);
+ goto out;
+ }
+
+ /* TODO: set estimation properties etc. on the Job object */
+
+ G_LOCK (object_lock);
+ still_in_progress = (g_strcmp0 (drive->smart_selftest_status, "inprogress") == 0);
+ G_UNLOCK (object_lock);
+ if (!still_in_progress)
+ {
+ ret = TRUE;
+ goto out;
+ }
+
+ /* Sleep for 30 seconds or until we're cancelled */
+ if (g_cancellable_make_pollfd (cancellable, &poll_fd))
+ {
+ gint poll_ret;
+ do
+ {
+ poll_ret = g_poll (&poll_fd, 1, 30 * 1000);
+ }
+ while (poll_ret == -1 && errno == EINTR);
+ g_cancellable_release_fd (cancellable);
+ }
+ else
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "Error creating pollfd for cancellable");
+ goto out;
+ }
+
+ /* Check if we're cancelled */
+ if (g_cancellable_is_cancelled (cancellable))
+ {
+ g_set_error (error,
+ UDISKS_ERROR,
+ UDISKS_ERROR_CANCELLED,
+ "Self-test was cancelled");
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+
+ out:
+ G_LOCK (object_lock);
+ drive->selftest_job = NULL;
+ if (drive->selftest_cancellable != NULL)
+ g_object_unref (drive->selftest_cancellable);
+ G_UNLOCK (object_lock);
+ return ret;
+}
+
+
+static gboolean
+handle_smart_selftest_start (UDisksDriveAta *_drive,
+ GDBusMethodInvocation *invocation,
+ const gchar *type,
+ GVariant *options)
+{
+ UDisksLinuxDriveObject *object;
+ UDisksLinuxBlockObject *block_object;
+ UDisksDaemon *daemon;
+ UDisksLinuxDriveAta *drive = UDISKS_LINUX_DRIVE_ATA (_drive);
+ GError *error;
+
+ object = UDISKS_LINUX_DRIVE_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (drive)));
+ daemon = udisks_linux_drive_object_get_daemon (object);
+ block_object = udisks_linux_drive_object_get_block (object, TRUE);
+ if (block_object == NULL)
{
g_dbus_method_invocation_return_error (invocation,
UDISKS_ERROR,
UDISKS_ERROR_FAILED,
- "SMART is not supported");
+ "Unable to find physical block device for drive");
goto out;
}
- if (!udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
+ if (!udisks_drive_ata_get_smart_supported (UDISKS_DRIVE_ATA (drive)) ||
+ !udisks_drive_ata_get_smart_enabled (UDISKS_DRIVE_ATA (drive)))
{
g_dbus_method_invocation_return_error (invocation,
UDISKS_ERROR,
UDISKS_ERROR_FAILED,
- "SMART is not enabled");
+ "SMART is not supported or enabled");
goto out;
}
+ G_LOCK (object_lock);
+ if (drive->selftest_job != NULL)
+ {
+ g_dbus_method_invocation_return_error (invocation,
+ UDISKS_ERROR,
+ UDISKS_ERROR_FAILED,
+ "There is already SMART self-test running");
+ G_UNLOCK (object_lock);
+ goto out;
+ }
+ G_UNLOCK (object_lock);
+
+ if (!udisks_daemon_util_check_authorization_sync (daemon,
+ UDISKS_OBJECT (block_object),
+ "org.freedesktop.udisks2.ata-smart-selftest",
+ options,
+ N_("Authentication is required to start a SMART self-test on $(udisks2.device)"),
+ invocation))
+ goto out;
+
error = NULL;
- if (!udisks_linux_drive_ata_refresh_smart_sync (drive,
- nowakeup,
- NULL /* cancellable */,
- &error))
+ if (!udisks_linux_drive_ata_smart_selftest_sync (drive,
+ type,
+ NULL, /* cancellable */
+ &error))
{
- udisks_warning ("Error updating ATA smart for %s: %s (%s, %d)",
- g_dbus_object_get_object_path (G_DBUS_OBJECT (drive)),
+ udisks_warning ("Error starting SMART selftest for %s: %s (%s, %d)",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (object)),
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 (UDISKS_DRIVE_ATA (drive), invocation);
+ G_LOCK (object_lock);
+ if (drive->selftest_job == NULL)
+ {
+ drive->selftest_cancellable = g_cancellable_new ();
+ drive->selftest_job = UDISKS_THREADED_JOB (udisks_daemon_launch_threaded_job (daemon,
+ selftest_job_func,
+ g_object_ref (drive),
+ g_object_unref,
+ drive->selftest_cancellable));
+ }
+ G_UNLOCK (object_lock);
+
+ udisks_drive_ata_complete_smart_selftest_start (UDISKS_DRIVE_ATA (drive), invocation);
out:
- if (block_object != NULL)
- g_object_unref (block_object);
return TRUE; /* returning TRUE means that we handled the method invocation */
}
drive_ata_iface_init (UDisksDriveAtaIface *iface)
{
iface->handle_smart_update = handle_smart_update;
+ iface->handle_smart_get_attributes = handle_smart_get_attributes;
+ iface->handle_smart_selftest_abort = handle_smart_selftest_abort;
+ iface->handle_smart_selftest_start = handle_smart_selftest_start;
}