port to libatasmart
authorDavid Zeuthen <davidz@redhat.com>
Mon, 23 Mar 2009 23:14:24 +0000 (19:14 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Mon, 23 Mar 2009 23:14:24 +0000 (19:14 -0400)
18 files changed:
configure.ac
policy/org.freedesktop.devicekit.disks.policy.in
src/95-devkit-disks.rules
src/Makefile.am
src/devkit-disks-daemon.c
src/devkit-disks-daemon.h
src/devkit-disks-device-private.c
src/devkit-disks-device-private.h
src/devkit-disks-device.c
src/devkit-disks-device.h
src/devkit-disks-logger.c [deleted file]
src/devkit-disks-logger.h [deleted file]
src/devkit-disks-types.h
src/job-ata-smart-collect.c [new file with mode: 0644]
src/job-ata-smart-selftest.c [new file with mode: 0644]
src/job-smart-selftest.c [deleted file]
src/org.freedesktop.DeviceKit.Disks.Device.xml
tools/devkit-disks.c

index f8dde93..6da4ed6 100644 (file)
@@ -139,9 +139,9 @@ PKG_CHECK_MODULES(DEVMAPPER, [devmapper >= 1.02])
 AC_SUBST(DEVMAPPER_CFLAGS)
 AC_SUBST(DEVMAPPER_LIBS)
 
-PKG_CHECK_MODULES(SQLITE3, [sqlite3])
-AC_SUBST(SQLITE3_CFLAGS)
-AC_SUBST(SQLITE3_LIBS)
+PKG_CHECK_MODULES(LIBATASMART, [libatasmart >= 0.2])
+AC_SUBST(LIBATASMART_CFLAGS)
+AC_SUBST(LIBATASMART_LIBS)
 
 if test "x$GCC" = "xyes"; then
   LDFLAGS="-Wl,--as-needed $LDFLAGS"
index 8a3fab6..361252e 100644 (file)
@@ -119,9 +119,9 @@ file are instantly applied.
     </defaults>
   </action>
 
-  <action id="org.freedesktop.devicekit.disks.drive-smart-refresh">
-    <_description>Refresh S.M.A.R.T. data</_description>
-    <_message>Authentication is required to refresh S.M.A.R.T. data</_message>
+  <action id="org.freedesktop.devicekit.disks.drive-ata-smart-refresh">
+    <_description>Refresh ATA SMART data</_description>
+    <_message>Authentication is required to refresh ATA SMART data</_message>
     <defaults>
       <allow_any>no</allow_any>
       <allow_inactive>no</allow_inactive>
@@ -129,9 +129,9 @@ file are instantly applied.
     </defaults>
   </action>
 
-  <action id="org.freedesktop.devicekit.disks.drive-smart-selftest">
-    <_description>Run S.M.A.R.T. Self Tests</_description>
-    <_message>Authentication is required to run S.M.A.R.T. self tests</_message>
+  <action id="org.freedesktop.devicekit.disks.drive-ata-smart-selftest">
+    <_description>Run ATA SMART Self Tests</_description>
+    <_message>Authentication is required to run ATA SMART self tests</_message>
     <defaults>
       <allow_any>no</allow_any>
       <allow_inactive>no</allow_inactive>
@@ -139,9 +139,9 @@ file are instantly applied.
     </defaults>
   </action>
 
-  <action id="org.freedesktop.devicekit.disks.drive-smart-retrieve-historical-data">
-    <_description>Retrieve historical S.M.A.R.T. data</_description>
-    <_message>Authentication is required to retrieve historical S.M.A.R.T. data</_message>
+  <action id="org.freedesktop.devicekit.disks.drive-ata-smart-retrieve-historical-data">
+    <_description>Retrieve historical ATA SMART data</_description>
+    <_message>Authentication is required to retrieve historical ATA SMART data</_message>
     <defaults>
       <allow_any>no</allow_any>
       <allow_inactive>no</allow_inactive>
index 5a78558..9e4b585 100644 (file)
@@ -63,12 +63,21 @@ LABEL="device_mapper_end"
 ##############################################################################################################
 
 # pick up data from MD components; this really should be done by rules
-* installed by mdadm or the kernel package
+# installed by mdadm or the kernel package
 #
 ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/sbin/mdadm --examine --export $tempnode"
 
 ##############################################################################################################
 
+# Check if disk is capable of ATA smart; this should probably be done by
+# ata_id and usb_id
+#
+
+ID_BUS="usb", DEVTYPE="disk", IMPORT{program}="devkit-disks-probe-ata-smart $tempnode"
+ID_BUS="scsi", ID_VENDOR="ATA", DEVTYPE="disk", IMPORT{program}="devkit-disks-probe-ata-smart $tempnode"
+
+##############################################################################################################
+
 # Example rule for tagging a device with a specific media type. Where and
 # how to store this database needs some thought.
 #
index ec6beec..834c01a 100644 (file)
@@ -1,5 +1,7 @@
 ## Process this file with automake to produce Makefile.in
 
+NULL =
+
 INCLUDES = \
        -I$(top_builddir)/src -I$(top_srcdir)/src \
        -DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \
@@ -42,7 +44,6 @@ devkit_disks_daemon_SOURCES =                                                 \
        devkit-disks-types.h                                            \
        devkit-disks-private.h                                          \
        devkit-disks-daemon.h           devkit-disks-daemon.c           \
-       devkit-disks-logger.h           devkit-disks-logger.c           \
        devkit-disks-device.h           devkit-disks-device.c           \
        devkit-disks-device-private.h   devkit-disks-device-private.c   \
        devkit-disks-mount-file.h       devkit-disks-mount-file.c       \
@@ -79,9 +80,11 @@ libexec_PROGRAMS += devkit-disks-helper-erase                        \
                    devkit-disks-helper-modify-partition                \
                    devkit-disks-helper-create-partition-table          \
                    devkit-disks-helper-change-filesystem-label         \
-                   devkit-disks-helper-smart-selftest                  \
                    devkit-disks-helper-linux-md-remove-component       \
-                   devkit-disks-helper-fstab-mounter
+                   devkit-disks-helper-fstab-mounter                   \
+                   devkit-disks-helper-ata-smart-collect               \
+                   devkit-disks-helper-ata-smart-selftest              \
+                   $(NULL)
 
 libexec_SCRIPTS = devkit-disks-helper-change-luks-password
 
@@ -113,9 +116,13 @@ devkit_disks_helper_change_filesystem_label_SOURCES = job-shared.h job-change-fi
 devkit_disks_helper_change_filesystem_label_CPPFLAGS = $(AM_CPPFLAGS)
 devkit_disks_helper_change_filesystem_label_LDADD = $(GLIB_LIBS)
 
-devkit_disks_helper_smart_selftest_SOURCES = job-shared.h job-smart-selftest.c
-devkit_disks_helper_smart_selftest_CPPFLAGS = $(AM_CPPFLAGS)
-devkit_disks_helper_smart_selftest_LDADD = $(GLIB_LIBS)
+devkit_disks_helper_ata_smart_selftest_SOURCES = job-shared.h job-ata-smart-selftest.c
+devkit_disks_helper_ata_smart_selftest_CPPFLAGS = $(AM_CPPFLAGS) $(LIBATASMART_CFLAGS) $(GLIB_CFLAGS)
+devkit_disks_helper_ata_smart_selftest_LDADD = $(LIBATASMART_LIBS) $(GLIB_LIBS)
+
+devkit_disks_helper_ata_smart_collect_SOURCES = job-ata-smart-collect.c
+devkit_disks_helper_ata_smart_collect_CPPFLAGS = $(AM_CPPFLAGS) $(LIBATASMART_CFLAGS) $(GLIB_CFLAGS)
+devkit_disks_helper_ata_smart_collect_LDADD = $(LIBATASMART_LIBS) $(GLIB_LIBS)
 
 devkit_disks_helper_linux_md_remove_component_SOURCES = job-shared.h job-linux-md-remove-component.c
 devkit_disks_helper_linux_md_remove_component_CPPFLAGS = $(AM_CPPFLAGS)
@@ -127,7 +134,7 @@ devkit_disks_helper_fstab_mounter_LDADD = $(GLIB_LIBS)
 
 # TODO: move to udev
 udevhelperdir = $(slashlibdir)/udev
-udevhelper_PROGRAMS = devkit-disks-part-id devkit-disks-dm-export
+udevhelper_PROGRAMS = devkit-disks-part-id devkit-disks-dm-export devkit-disks-probe-ata-smart
 
 devkit_disks_part_id_SOURCES = part-id.c
 devkit_disks_part_id_CPPFLAGS = $(AM_CPPFLAGS)
@@ -136,6 +143,10 @@ devkit_disks_part_id_LDADD = $(GLIB_LIBS) libpartutil.la
 devkit_disks_dm_export_SOURCES = devkit-disks-dm-export.c
 devkit_disks_dm_export_CPPFLAGS = $(AM_CPPFLAGS) $(DEVMAPPER_CFLAGS)
 devkit_disks_dm_export_LDADD = $(DEVMAPPER_LIBS)
+
+devkit_disks_probe_ata_smart_SOURCES = devkit-disks-probe-ata-smart.c
+devkit_disks_probe_ata_smart_CPPFLAGS = $(AM_CPPFLAGS) $(LIBATASMART_CFLAGS)
+devkit_disks_probe_ata_smart_LDADD = $(LIBATASMART_LIBS)
 # end move to udev
 
 
@@ -171,4 +182,5 @@ clean-local :
 
 install-data-local:
        -$(mkdir_p) $(DESTDIR)$(localstatedir)/lib/DeviceKit-disks
-       -chmod 0755 $(DESTDIR)$(localstatedir)/lib/DeviceKit-disks
+       -chmod 0700 $(DESTDIR)$(localstatedir)/lib/DeviceKit-disks
+       -$(mkdir_p) $(DESTDIR)$(localstatedir)/lib/DeviceKit-disks/ata-smart
index 21b9a49..f8b63d5 100644 (file)
@@ -57,7 +57,6 @@
 #include "devkit-disks-mount-monitor.h"
 #include "devkit-disks-poller.h"
 #include "devkit-disks-inhibitor.h"
-#include "devkit-disks-logger.h"
 
 #include "devkit-disks-daemon-glue.h"
 #include "devkit-disks-marshal.h"
@@ -103,9 +102,7 @@ struct DevkitDisksDaemonPrivate
 
         DevkitDisksMountMonitor *mount_monitor;
 
-        guint                    smart_refresh_timer_id;
-
-        DevkitDisksLogger       *logger;
+        guint                    ata_smart_refresh_timer_id;
 
         GList *polling_inhibitors;
 
@@ -170,9 +167,10 @@ devkit_disks_error_get_type (void)
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_NOT_LINUX_MD, "NotLinuxMd"),
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_NOT_LINUX_MD_COMPONENT, "NotLinuxComponent"),
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_NOT_DRIVE, "NotDrive"),
-                                ENUM_ENTRY (DEVKIT_DISKS_ERROR_NOT_SMART_CAPABLE, "NotSmartCapable"),
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_NOT_SUPPORTED, "NotSupported"),
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_NOT_FOUND, "NotFound"),
+                                ENUM_ENTRY (DEVKIT_DISKS_ERROR_ATA_SMART_NOT_AVAILABLE, "AtaSmartNotAvailable"),
+                                ENUM_ENTRY (DEVKIT_DISKS_ERROR_ATA_SMART_WOULD_WAKEUP, "AtaSmartWouldWakeup"),
 
                                 { 0, 0, 0 }
                         };
@@ -573,12 +571,8 @@ devkit_disks_daemon_finalize (GObject *object)
                 g_object_unref (daemon->priv->devkit_client);
         }
 
-        if (daemon->priv->smart_refresh_timer_id > 0) {
-                g_source_remove (daemon->priv->smart_refresh_timer_id);
-        }
-
-        if (daemon->priv->logger != NULL) {
-                g_object_unref (daemon->priv->logger);
+        if (daemon->priv->ata_smart_refresh_timer_id > 0) {
+                g_source_remove (daemon->priv->ata_smart_refresh_timer_id);
         }
 
         for (l = daemon->priv->polling_inhibitors; l != NULL; l = l->next) {
@@ -940,7 +934,7 @@ out:
 }
 
 static gboolean
-refresh_smart_data (DevkitDisksDaemon *daemon)
+refresh_ata_smart_data (DevkitDisksDaemon *daemon)
 {
         DevkitDisksDevice *device;
         const char *native_path;
@@ -948,18 +942,20 @@ refresh_smart_data (DevkitDisksDaemon *daemon)
 
         g_hash_table_iter_init (&iter, daemon->priv->map_native_path_to_device);
         while (g_hash_table_iter_next (&iter, (gpointer *) &native_path, (gpointer *) &device)) {
-                if (device->priv->drive_smart_is_capable) {
-                        char *options[] = {NULL};
+                if (device->priv->drive_ata_smart_is_available) {
+                        char *options[] = {"nowakeup", NULL};
 
-                        g_debug ("automatically refreshing SMART data for %s", native_path);
-                        devkit_disks_device_drive_smart_refresh_data (device, options, NULL);
+                        g_debug ("refreshing ATA SMART data for %s", native_path);
+
+                        devkit_disks_device_drive_ata_smart_refresh_data (device, options, NULL);
                 }
         }
 
         /* update in another 30 minutes */
-        daemon->priv->smart_refresh_timer_id = g_timeout_add_seconds (30 * 60,
-                                                                      (GSourceFunc) refresh_smart_data,
-                                                                      daemon);
+        daemon->priv->ata_smart_refresh_timer_id = g_timeout_add_seconds (30 * 60,
+                                                                          (GSourceFunc) refresh_ata_smart_data,
+                                                                          daemon);
+
         return FALSE;
 }
 
@@ -1074,8 +1070,6 @@ register_disks_daemon (DevkitDisksDaemon *daemon)
         g_signal_connect (daemon->priv->mount_monitor, "mounted", (GCallback) mount_added, daemon);
         g_signal_connect (daemon->priv->mount_monitor, "unmounted", (GCallback) mount_removed, daemon);
 
-        daemon->priv->logger = devkit_disks_logger_new ();
-
         return TRUE;
 error:
         return FALSE;
@@ -1133,8 +1127,10 @@ devkit_disks_daemon_new (void)
         devkit_disks_mount_file_clean_stale (l);
         g_list_free (l);
 
-        /* initial refresh of SMART data */
-        refresh_smart_data (daemon);
+        /* set up timer for ATA smart data refresh */
+        daemon->priv->ata_smart_refresh_timer_id = g_timeout_add_seconds (30 * 60,
+                                                                          (GSourceFunc) refresh_ata_smart_data,
+                                                                          daemon);
 
         return daemon;
 
@@ -1142,12 +1138,6 @@ devkit_disks_daemon_new (void)
         return NULL;
 }
 
-DevkitDisksLogger *
-devkit_disks_daemon_local_get_logger (DevkitDisksDaemon *daemon)
-{
-        return daemon->priv->logger;
-}
-
 DevkitDisksMountMonitor *
 devkit_disks_daemon_local_get_mount_monitor (DevkitDisksDaemon *daemon)
 {
index c29c6c4..81b5ea0 100644 (file)
@@ -70,9 +70,10 @@ typedef enum
         DEVKIT_DISKS_ERROR_NOT_LINUX_MD,
         DEVKIT_DISKS_ERROR_NOT_LINUX_MD_COMPONENT,
         DEVKIT_DISKS_ERROR_NOT_DRIVE,
-        DEVKIT_DISKS_ERROR_NOT_SMART_CAPABLE,
         DEVKIT_DISKS_ERROR_NOT_SUPPORTED,
         DEVKIT_DISKS_ERROR_NOT_FOUND,
+        DEVKIT_DISKS_ERROR_ATA_SMART_NOT_AVAILABLE,
+        DEVKIT_DISKS_ERROR_ATA_SMART_WOULD_WAKEUP,
         DEVKIT_DISKS_NUM_ERRORS
 } DevkitDisksError;
 
@@ -121,8 +122,6 @@ gboolean           devkit_disks_daemon_local_has_polling_inhibitors (DevkitDisks
 
 gboolean           devkit_disks_daemon_local_has_inhibitors (DevkitDisksDaemon       *daemon);
 
-DevkitDisksLogger *devkit_disks_daemon_local_get_logger (DevkitDisksDaemon *daemon);
-
 DevkitDisksMountMonitor *devkit_disks_daemon_local_get_mount_monitor (DevkitDisksDaemon *daemon);
 
 typedef struct {
index 843c98d..14735dc 100644 (file)
@@ -1131,88 +1131,206 @@ devkit_disks_device_set_holders_objpath (DevkitDisksDevice *device, GStrv value)
     }
 }
 
+
 void
-devkit_disks_device_set_drive_smart_is_capable (DevkitDisksDevice *device, gboolean value)
+devkit_disks_device_set_drive_ata_smart_is_available (DevkitDisksDevice *device, gboolean value)
 {
-  if (G_UNLIKELY (device->priv->drive_smart_is_capable != value))
+  if (G_UNLIKELY (device->priv->drive_ata_smart_is_available != value))
     {
-      device->priv->drive_smart_is_capable = value;
-      emit_changed (device, "drive_smart_is_capable");
+      device->priv->drive_ata_smart_is_available = value;
+      emit_changed (device, "drive_ata_smart_is_available");
     }
 }
 
 void
-devkit_disks_device_set_drive_smart_is_enabled (DevkitDisksDevice *device, gboolean value)
+devkit_disks_device_set_drive_ata_smart_is_failing (DevkitDisksDevice *device, gboolean value)
 {
-  if (G_UNLIKELY (device->priv->drive_smart_is_enabled != value))
+  if (G_UNLIKELY (device->priv->drive_ata_smart_is_failing != value))
     {
-      device->priv->drive_smart_is_enabled = value;
-      emit_changed (device, "drive_smart_is_enabled");
+      device->priv->drive_ata_smart_is_failing = value;
+      emit_changed (device, "drive_ata_smart_is_failing");
     }
 }
 
 void
-devkit_disks_device_set_drive_smart_time_collected (DevkitDisksDevice *device, guint64 value)
+devkit_disks_device_set_drive_ata_smart_is_failing_valid (DevkitDisksDevice *device, gboolean value)
 {
-  if (G_UNLIKELY (device->priv->drive_smart_time_collected != value))
+  if (G_UNLIKELY (device->priv->drive_ata_smart_is_failing_valid != value))
     {
-      device->priv->drive_smart_time_collected = value;
-      emit_changed (device, "drive_smart_time_collected");
+      device->priv->drive_ata_smart_is_failing_valid = value;
+      emit_changed (device, "drive_ata_smart_is_failing_valid");
     }
 }
 
 void
-devkit_disks_device_set_drive_smart_is_failing (DevkitDisksDevice *device, gboolean value)
+devkit_disks_device_set_drive_ata_smart_has_bad_sectors (DevkitDisksDevice *device, gboolean value)
 {
-  if (G_UNLIKELY (device->priv->drive_smart_is_failing != value))
+  if (G_UNLIKELY (device->priv->drive_ata_smart_has_bad_sectors != value))
     {
-      device->priv->drive_smart_is_failing = value;
-      emit_changed (device, "drive_smart_is_failing");
+      device->priv->drive_ata_smart_has_bad_sectors = value;
+      emit_changed (device, "drive_ata_smart_has_bad_sectors");
     }
 }
 
 void
-devkit_disks_device_set_drive_smart_temperature (DevkitDisksDevice *device, gdouble value)
+devkit_disks_device_set_drive_ata_smart_has_bad_attributes (DevkitDisksDevice *device, gboolean value)
 {
-  if (G_UNLIKELY (device->priv->drive_smart_temperature != value))
+  if (G_UNLIKELY (device->priv->drive_ata_smart_has_bad_attributes != value))
     {
-      device->priv->drive_smart_temperature = value;
-      emit_changed (device, "drive_smart_temperature");
+      device->priv->drive_ata_smart_has_bad_attributes = value;
+      emit_changed (device, "drive_ata_smart_has_bad_attributes");
     }
 }
 
 void
-devkit_disks_device_set_drive_smart_time_powered_on (DevkitDisksDevice *device, guint64 value)
+devkit_disks_device_set_drive_ata_smart_temperature_kelvin (DevkitDisksDevice *device, gdouble value)
 {
-  if (G_UNLIKELY (device->priv->drive_smart_time_powered_on != value))
+  if (G_UNLIKELY (device->priv->drive_ata_smart_temperature_kelvin != value))
     {
-      device->priv->drive_smart_time_powered_on = value;
-      emit_changed (device, "drive_smart_time_powered_on");
+      device->priv->drive_ata_smart_temperature_kelvin = value;
+      emit_changed (device, "drive_ata_smart_temperature_kelvin");
     }
 }
 
 void
-devkit_disks_device_set_drive_smart_last_self_test_result (DevkitDisksDevice *device, const gchar *value)
+devkit_disks_device_set_drive_ata_smart_power_on_seconds (DevkitDisksDevice *device, guint64 value)
 {
-  if (G_UNLIKELY (g_strcmp0 (device->priv->drive_smart_last_self_test_result, value) != 0))
+  if (G_UNLIKELY (device->priv->drive_ata_smart_power_on_seconds != value))
     {
-      g_free (device->priv->drive_smart_last_self_test_result);
-      device->priv->drive_smart_last_self_test_result = g_strdup (value);
-      emit_changed (device, "drive_smart_last_self_test_result");
+      device->priv->drive_ata_smart_power_on_seconds = value;
+      emit_changed (device, "drive_ata_smart_power_on_seconds");
     }
 }
 
-/* NOTE: steals attributes! */
 void
-devkit_disks_device_set_drive_smart_attributes_steal (DevkitDisksDevice *device, GPtrArray *attributes)
+devkit_disks_device_set_drive_ata_smart_time_collected (DevkitDisksDevice *device, guint64 value)
 {
-  /* TODO: compare? Not really needed, this happens very rarely */
+  if (G_UNLIKELY (device->priv->drive_ata_smart_time_collected != value))
+    {
+      device->priv->drive_ata_smart_time_collected = value;
+      emit_changed (device, "drive_ata_smart_time_collected");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_offline_data_collection_status (DevkitDisksDevice *device, guint value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_offline_data_collection_status != value))
+    {
+      device->priv->drive_ata_smart_offline_data_collection_status = value;
+      emit_changed (device, "drive_ata_smart_offline_data_collection_status");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_offline_data_collection_seconds (DevkitDisksDevice *device, guint value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_offline_data_collection_seconds != value))
+    {
+      device->priv->drive_ata_smart_offline_data_collection_seconds = value;
+      emit_changed (device, "drive_ata_smart_offline_data_collection_seconds");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_self_test_execution_status (DevkitDisksDevice *device, guint value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_self_test_execution_status != value))
+    {
+      device->priv->drive_ata_smart_self_test_execution_status = value;
+      emit_changed (device, "drive_ata_smart_self_test_execution_status");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_self_test_execution_percent_remaining (DevkitDisksDevice *device, guint value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_self_test_execution_percent_remaining != value))
+    {
+      device->priv->drive_ata_smart_self_test_execution_percent_remaining = value;
+      emit_changed (device, "drive_ata_smart_self_test_execution_percent_remaining");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_short_and_extended_self_test_available (DevkitDisksDevice *device, gboolean value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_short_and_extended_self_test_available != value))
+    {
+      device->priv->drive_ata_smart_short_and_extended_self_test_available = value;
+      emit_changed (device, "drive_ata_smart_short_and_extended_self_test_available");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_conveyance_self_test_available (DevkitDisksDevice *device, gboolean value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_conveyance_self_test_available != value))
+    {
+      device->priv->drive_ata_smart_conveyance_self_test_available = value;
+      emit_changed (device, "drive_ata_smart_conveyance_self_test_available");
+    }
+}
 
-  g_ptr_array_foreach (device->priv->drive_smart_attributes, (GFunc) g_value_array_free, NULL);
-  g_ptr_array_free (device->priv->drive_smart_attributes, TRUE);
+void
+devkit_disks_device_set_drive_ata_smart_start_self_test_available (DevkitDisksDevice *device, gboolean value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_start_self_test_available != value))
+    {
+      device->priv->drive_ata_smart_start_self_test_available = value;
+      emit_changed (device, "drive_ata_smart_start_self_test_available");
+    }
+}
 
-  device->priv->drive_smart_attributes = attributes;
+void
+devkit_disks_device_set_drive_ata_smart_abort_self_test_available (DevkitDisksDevice *device, gboolean value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_abort_self_test_available != value))
+    {
+      device->priv->drive_ata_smart_abort_self_test_available = value;
+      emit_changed (device, "drive_ata_smart_abort_self_test_available");
+    }
+}
 
-  emit_changed (device, "drive_smart_attributes");
+void
+devkit_disks_device_set_drive_ata_smart_short_self_test_polling_minutes (DevkitDisksDevice *device, guint value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_short_self_test_polling_minutes != value))
+    {
+      device->priv->drive_ata_smart_short_self_test_polling_minutes = value;
+      emit_changed (device, "drive_ata_smart_short_self_test_polling_minutes");
+    }
 }
 
+void
+devkit_disks_device_set_drive_ata_smart_extended_self_test_polling_minutes (DevkitDisksDevice *device, guint value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_extended_self_test_polling_minutes != value))
+    {
+      device->priv->drive_ata_smart_extended_self_test_polling_minutes = value;
+      emit_changed (device, "drive_ata_smart_extended_self_test_polling_minutes");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_conveyance_self_test_polling_minutes (DevkitDisksDevice *device, guint value)
+{
+  if (G_UNLIKELY (device->priv->drive_ata_smart_conveyance_self_test_polling_minutes != value))
+    {
+      device->priv->drive_ata_smart_conveyance_self_test_polling_minutes = value;
+      emit_changed (device, "drive_ata_smart_conveyance_self_test_polling_minutes");
+    }
+}
+
+void
+devkit_disks_device_set_drive_ata_smart_attributes_steal (DevkitDisksDevice *device, GPtrArray *attributes)
+{
+  /* TODO: compare? Not really needed, this happens very rarely */
+
+  g_ptr_array_foreach (device->priv->drive_ata_smart_attributes, (GFunc) g_value_array_free, NULL);
+  g_ptr_array_free (device->priv->drive_ata_smart_attributes, TRUE);
+
+  device->priv->drive_ata_smart_attributes = attributes;
+
+  emit_changed (device, "drive_ata_smart_attributes");
+}
index 3f30797..6774ad5 100644 (file)
@@ -32,24 +32,29 @@ G_BEGIN_DECLS
 struct Job;
 typedef struct Job Job;
 
-#define SMART_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_STRING,   \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_STRING,   \
-                                                        G_TYPE_INVALID))
-
-#define HISTORICAL_SMART_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
-                                                                   G_TYPE_UINT64, \
-                                                                   G_TYPE_DOUBLE, \
-                                                                   G_TYPE_UINT64, \
-                                                                   G_TYPE_STRING, \
-                                                                   G_TYPE_BOOLEAN, \
-                                                                   dbus_g_type_get_collection ("GPtrArray", SMART_DATA_STRUCT_TYPE), \
-                                                                   G_TYPE_INVALID))
+#define ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray", \
+                                                                      G_TYPE_UINT, \
+                                                                      G_TYPE_STRING, \
+                                                                      G_TYPE_UINT, \
+                                                                      G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UCHAR, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UCHAR, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UCHAR, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UINT, G_TYPE_UINT64, \
+                                                                      dbus_g_type_get_collection ("GArray", G_TYPE_UCHAR), \
+                                                                      G_TYPE_INVALID))
+
+#define ATA_SMART_HISTORICAL_SMART_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
+                                                                             G_TYPE_UINT64, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_DOUBLE, \
+                                                                             G_TYPE_UINT64, \
+                                                                             dbus_g_type_get_collection ("GPtrArray", ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE), \
+                                                                             G_TYPE_INVALID))
 
 #define LSOF_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
                                                        G_TYPE_UINT,     \
@@ -189,15 +194,26 @@ struct DevkitDisksDevicePrivate
         double linux_md_sync_percentage;
         guint64 linux_md_sync_speed;
 
-        /* We want S.M.A.R.T. to persist over change events */
-        gboolean drive_smart_is_capable;
-        gboolean drive_smart_is_enabled;
-        guint64 drive_smart_time_collected;
-        gboolean drive_smart_is_failing;
-        double drive_smart_temperature;
-        guint64 drive_smart_time_powered_on;
-        char *drive_smart_last_self_test_result;
-        GPtrArray *drive_smart_attributes;
+        gboolean drive_ata_smart_is_available;
+        gboolean drive_ata_smart_is_failing;
+        gboolean drive_ata_smart_is_failing_valid;
+        gboolean drive_ata_smart_has_bad_sectors;
+        gboolean drive_ata_smart_has_bad_attributes;
+        gdouble drive_ata_smart_temperature_kelvin;
+        guint64 drive_ata_smart_power_on_seconds;
+        guint64 drive_ata_smart_time_collected;
+        guint drive_ata_smart_offline_data_collection_status;
+        guint drive_ata_smart_offline_data_collection_seconds;
+        guint drive_ata_smart_self_test_execution_status;
+        guint drive_ata_smart_self_test_execution_percent_remaining;
+        gboolean drive_ata_smart_short_and_extended_self_test_available;
+        gboolean drive_ata_smart_conveyance_self_test_available;
+        gboolean drive_ata_smart_start_self_test_available;
+        gboolean drive_ata_smart_abort_self_test_available;
+        guint drive_ata_smart_short_self_test_polling_minutes;
+        guint drive_ata_smart_extended_self_test_polling_minutes;
+        guint drive_ata_smart_conveyance_self_test_polling_minutes;
+        GPtrArray *drive_ata_smart_attributes;
 
         /* the following properties are not (yet) exported */
         char *dm_name;
@@ -314,14 +330,27 @@ void devkit_disks_device_set_dm_name (DevkitDisksDevice *device, const gchar *va
 void devkit_disks_device_set_slaves_objpath (DevkitDisksDevice *device, GStrv value);
 void devkit_disks_device_set_holders_objpath (DevkitDisksDevice *device, GStrv value);
 
-void devkit_disks_device_set_drive_smart_is_capable (DevkitDisksDevice *device, gboolean value);
-void devkit_disks_device_set_drive_smart_is_enabled (DevkitDisksDevice *device, gboolean value);
-void devkit_disks_device_set_drive_smart_time_collected (DevkitDisksDevice *device, guint64 value);
-void devkit_disks_device_set_drive_smart_is_failing (DevkitDisksDevice *device, gboolean value);
-void devkit_disks_device_set_drive_smart_temperature (DevkitDisksDevice *device, gdouble value);
-void devkit_disks_device_set_drive_smart_time_powered_on (DevkitDisksDevice *device, guint64 value);
-void devkit_disks_device_set_drive_smart_last_self_test_result (DevkitDisksDevice *device, const gchar *value);
-void devkit_disks_device_set_drive_smart_attributes_steal (DevkitDisksDevice *device, GPtrArray *attributes);
+void devkit_disks_device_set_drive_ata_smart_is_available (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_is_failing (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_is_failing_valid (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_has_bad_sectors (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_has_bad_attributes (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_temperature_kelvin (DevkitDisksDevice *device, gdouble value);
+void devkit_disks_device_set_drive_ata_smart_power_on_seconds (DevkitDisksDevice *device, guint64 value);
+void devkit_disks_device_set_drive_ata_smart_time_collected (DevkitDisksDevice *device, guint64 value);
+void devkit_disks_device_set_drive_ata_smart_offline_data_collection_status (DevkitDisksDevice *device, guint value);
+void devkit_disks_device_set_drive_ata_smart_offline_data_collection_seconds (DevkitDisksDevice *device, guint value);
+void devkit_disks_device_set_drive_ata_smart_self_test_execution_status (DevkitDisksDevice *device, guint value);
+void devkit_disks_device_set_drive_ata_smart_self_test_execution_percent_remaining (DevkitDisksDevice *device, guint value);
+void devkit_disks_device_set_drive_ata_smart_short_and_extended_self_test_available (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_conveyance_self_test_available (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_start_self_test_available (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_abort_self_test_available (DevkitDisksDevice *device, gboolean value);
+void devkit_disks_device_set_drive_ata_smart_short_self_test_polling_minutes (DevkitDisksDevice *device, guint value);
+void devkit_disks_device_set_drive_ata_smart_extended_self_test_polling_minutes (DevkitDisksDevice *device, guint value);
+void devkit_disks_device_set_drive_ata_smart_conveyance_self_test_polling_minutes (DevkitDisksDevice *device, guint value);
+void devkit_disks_device_set_drive_ata_smart_attributes_steal (DevkitDisksDevice *device, GPtrArray *attributes);
+
 
 G_END_DECLS
 
index e0c711f..d5bae0c 100644 (file)
@@ -51,7 +51,6 @@
 #include <sqlite3.h>
 
 #include "devkit-disks-daemon.h"
-#include "devkit-disks-logger.h"
 #include "devkit-disks-device.h"
 #include "devkit-disks-device-private.h"
 #include "devkit-disks-marshal.h"
@@ -213,14 +212,26 @@ enum
         PROP_OPTICAL_DISC_NUM_AUDIO_TRACKS,
         PROP_OPTICAL_DISC_NUM_SESSIONS,
 
-        PROP_DRIVE_SMART_IS_CAPABLE,
-        PROP_DRIVE_SMART_IS_ENABLED,
-        PROP_DRIVE_SMART_TIME_COLLECTED,
-        PROP_DRIVE_SMART_IS_FAILING,
-        PROP_DRIVE_SMART_TEMPERATURE,
-        PROP_DRIVE_SMART_TIME_POWERED_ON,
-        PROP_DRIVE_SMART_LAST_SELF_TEST_RESULT,
-        PROP_DRIVE_SMART_ATTRIBUTES,
+        PROP_DRIVE_ATA_SMART_IS_AVAILABLE,
+        PROP_DRIVE_ATA_SMART_IS_FAILING,
+        PROP_DRIVE_ATA_SMART_IS_FAILING_VALID,
+        PROP_DRIVE_ATA_SMART_HAS_BAD_SECTORS,
+        PROP_DRIVE_ATA_SMART_HAS_BAD_ATTRIBUTES,
+        PROP_DRIVE_ATA_SMART_TEMPERATURE_KELVIN,
+        PROP_DRIVE_ATA_SMART_POWER_ON_SECONDS,
+        PROP_DRIVE_ATA_SMART_TIME_COLLECTED,
+        PROP_DRIVE_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS,
+        PROP_DRIVE_ATA_SMART_OFFLINE_DATA_COLLECTION_SECONDS,
+        PROP_DRIVE_ATA_SMART_SELF_TEST_EXECUTION_STATUS,
+        PROP_DRIVE_ATA_SMART_SELF_TEST_EXECUTION_PERCENT_REMAINING,
+        PROP_DRIVE_ATA_SMART_SHORT_AND_EXTENDED_SELF_TEST_AVAILABLE,
+        PROP_DRIVE_ATA_SMART_CONVEYANCE_SELF_TEST_AVAILABLE,
+        PROP_DRIVE_ATA_SMART_START_SELF_TEST_AVAILABLE,
+        PROP_DRIVE_ATA_SMART_ABORT_SELF_TEST_AVAILABLE,
+        PROP_DRIVE_ATA_SMART_SHORT_SELF_TEST_POLLING_MINUTES,
+        PROP_DRIVE_ATA_SMART_EXTENDED_SELF_TEST_POLLING_MINUTES,
+        PROP_DRIVE_ATA_SMART_CONVEYANCE_SELF_TEST_POLLING_MINUTES,
+        PROP_DRIVE_ATA_SMART_ATTRIBUTES,
 
         PROP_LINUX_MD_COMPONENT_LEVEL,
         PROP_LINUX_MD_COMPONENT_NUM_RAID_DEVICES,
@@ -526,29 +537,65 @@ get_property (GObject         *object,
                g_value_set_uint (value, device->priv->optical_disc_num_sessions);
                break;
 
-       case PROP_DRIVE_SMART_IS_CAPABLE:
-               g_value_set_boolean (value, device->priv->drive_smart_is_capable);
+       case PROP_DRIVE_ATA_SMART_IS_AVAILABLE:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_is_available);
                break;
-       case PROP_DRIVE_SMART_IS_ENABLED:
-               g_value_set_boolean (value, device->priv->drive_smart_is_enabled);
+       case PROP_DRIVE_ATA_SMART_IS_FAILING:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_is_failing);
                break;
-       case PROP_DRIVE_SMART_TIME_COLLECTED:
-               g_value_set_uint64 (value, device->priv->drive_smart_time_collected);
+       case PROP_DRIVE_ATA_SMART_IS_FAILING_VALID:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_is_failing_valid);
                break;
-       case PROP_DRIVE_SMART_IS_FAILING:
-               g_value_set_boolean (value, device->priv->drive_smart_is_failing);
+       case PROP_DRIVE_ATA_SMART_HAS_BAD_SECTORS:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_has_bad_sectors);
                break;
-       case PROP_DRIVE_SMART_TEMPERATURE:
-               g_value_set_double (value, device->priv->drive_smart_temperature);
+       case PROP_DRIVE_ATA_SMART_HAS_BAD_ATTRIBUTES:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_has_bad_attributes);
                break;
-       case PROP_DRIVE_SMART_TIME_POWERED_ON:
-               g_value_set_uint64 (value, device->priv->drive_smart_time_powered_on);
+       case PROP_DRIVE_ATA_SMART_TEMPERATURE_KELVIN:
+               g_value_set_double (value, device->priv->drive_ata_smart_temperature_kelvin);
                break;
-       case PROP_DRIVE_SMART_LAST_SELF_TEST_RESULT:
-               g_value_set_string (value, device->priv->drive_smart_last_self_test_result);
+       case PROP_DRIVE_ATA_SMART_POWER_ON_SECONDS:
+               g_value_set_uint64 (value, device->priv->drive_ata_smart_power_on_seconds);
                break;
-       case PROP_DRIVE_SMART_ATTRIBUTES:
-               g_value_set_boxed (value, device->priv->drive_smart_attributes);
+       case PROP_DRIVE_ATA_SMART_TIME_COLLECTED:
+               g_value_set_uint64 (value, device->priv->drive_ata_smart_time_collected);
+               break;
+       case PROP_DRIVE_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS:
+               g_value_set_uint (value, device->priv->drive_ata_smart_offline_data_collection_status);
+               break;
+       case PROP_DRIVE_ATA_SMART_OFFLINE_DATA_COLLECTION_SECONDS:
+               g_value_set_uint (value, device->priv->drive_ata_smart_offline_data_collection_seconds);
+               break;
+       case PROP_DRIVE_ATA_SMART_SELF_TEST_EXECUTION_STATUS:
+               g_value_set_uint (value, device->priv->drive_ata_smart_self_test_execution_status);
+               break;
+       case PROP_DRIVE_ATA_SMART_SELF_TEST_EXECUTION_PERCENT_REMAINING:
+               g_value_set_uint (value, device->priv->drive_ata_smart_self_test_execution_percent_remaining);
+               break;
+       case PROP_DRIVE_ATA_SMART_SHORT_AND_EXTENDED_SELF_TEST_AVAILABLE:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_short_and_extended_self_test_available);
+               break;
+       case PROP_DRIVE_ATA_SMART_CONVEYANCE_SELF_TEST_AVAILABLE:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_conveyance_self_test_available);
+               break;
+       case PROP_DRIVE_ATA_SMART_START_SELF_TEST_AVAILABLE:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_start_self_test_available);
+               break;
+       case PROP_DRIVE_ATA_SMART_ABORT_SELF_TEST_AVAILABLE:
+               g_value_set_boolean (value, device->priv->drive_ata_smart_abort_self_test_available);
+               break;
+       case PROP_DRIVE_ATA_SMART_SHORT_SELF_TEST_POLLING_MINUTES:
+               g_value_set_uint (value, device->priv->drive_ata_smart_short_self_test_polling_minutes);
+               break;
+       case PROP_DRIVE_ATA_SMART_EXTENDED_SELF_TEST_POLLING_MINUTES:
+               g_value_set_uint (value, device->priv->drive_ata_smart_extended_self_test_polling_minutes);
+               break;
+       case PROP_DRIVE_ATA_SMART_CONVEYANCE_SELF_TEST_POLLING_MINUTES:
+               g_value_set_uint (value, device->priv->drive_ata_smart_conveyance_self_test_polling_minutes);
+               break;
+       case PROP_DRIVE_ATA_SMART_ATTRIBUTES:
+               g_value_set_boxed (value, device->priv->drive_ata_smart_attributes);
                break;
 
        case PROP_LINUX_MD_COMPONENT_LEVEL:
@@ -981,39 +1028,88 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
 
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_IS_CAPABLE,
-                g_param_spec_boolean ("drive-smart-is-capable", NULL, NULL, FALSE, G_PARAM_READABLE));
+                PROP_DRIVE_ATA_SMART_IS_AVAILABLE,
+                g_param_spec_boolean ("drive-ata-smart-is-available", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_IS_FAILING,
+                g_param_spec_boolean ("drive-ata-smart-is-failing", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_IS_FAILING_VALID,
+                g_param_spec_boolean ("drive-ata-smart-is-failing-valid", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_HAS_BAD_SECTORS,
+                g_param_spec_boolean ("drive-ata-smart-has-bad-sectors", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_HAS_BAD_ATTRIBUTES,
+                g_param_spec_boolean ("drive-ata-smart-has-bad-attributes", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_TEMPERATURE_KELVIN,
+                g_param_spec_double ("drive-ata-smart-temperature-kelvin", NULL, NULL, 0, G_MAXDOUBLE, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_POWER_ON_SECONDS,
+                g_param_spec_uint64 ("drive-ata-smart-power-on-seconds", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_TIME_COLLECTED,
+                g_param_spec_uint64 ("drive-ata-smart-time-collected", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS,
+                g_param_spec_uint ("drive-ata-smart-offline-data-collection-status", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_OFFLINE_DATA_COLLECTION_SECONDS,
+                g_param_spec_uint ("drive-ata-smart-offline-data-collection-seconds", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_SELF_TEST_EXECUTION_STATUS,
+                g_param_spec_uint ("drive-ata-smart-self-test-execution-status", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_SELF_TEST_EXECUTION_PERCENT_REMAINING,
+                g_param_spec_uint ("drive-ata-smart-self-test-execution-percent-remaining", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DRIVE_ATA_SMART_SHORT_AND_EXTENDED_SELF_TEST_AVAILABLE,
+                g_param_spec_boolean ("drive-ata-smart-short-and-extended-self-test-available", NULL, NULL, FALSE, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_IS_ENABLED,
-                g_param_spec_boolean ("drive-smart-is-enabled", NULL, NULL, FALSE, G_PARAM_READABLE));
+                PROP_DRIVE_ATA_SMART_CONVEYANCE_SELF_TEST_AVAILABLE,
+                g_param_spec_boolean ("drive-ata-smart-conveyance-self-test-available", NULL, NULL, FALSE, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_TIME_COLLECTED,
-                g_param_spec_uint64 ("drive-smart-time-collected", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE));
+                PROP_DRIVE_ATA_SMART_START_SELF_TEST_AVAILABLE,
+                g_param_spec_boolean ("drive-ata-smart-start-self-test-available", NULL, NULL, FALSE, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_IS_FAILING,
-                g_param_spec_boolean ("drive-smart-is-failing", NULL, NULL, FALSE, G_PARAM_READABLE));
+                PROP_DRIVE_ATA_SMART_ABORT_SELF_TEST_AVAILABLE,
+                g_param_spec_boolean ("drive-ata-smart-abort-self-test-available", NULL, NULL, FALSE, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_TEMPERATURE,
-                g_param_spec_double ("drive-smart-temperature", NULL, NULL, -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READABLE));
+                PROP_DRIVE_ATA_SMART_SHORT_SELF_TEST_POLLING_MINUTES,
+                g_param_spec_uint ("drive-ata-smart-short-self-test-polling-minutes", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_TIME_POWERED_ON,
-                g_param_spec_uint64 ("drive-smart-time-powered-on", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READABLE));
+                PROP_DRIVE_ATA_SMART_EXTENDED_SELF_TEST_POLLING_MINUTES,
+                g_param_spec_uint ("drive-ata-smart-extended-self-test-polling-minutes", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_LAST_SELF_TEST_RESULT,
-                g_param_spec_string ("drive-smart-last-self-test-result", NULL, NULL, NULL, G_PARAM_READABLE));
+                PROP_DRIVE_ATA_SMART_CONVEYANCE_SELF_TEST_POLLING_MINUTES,
+                g_param_spec_uint ("drive-ata-smart-conveyance-self-test-polling-minutes", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
-                PROP_DRIVE_SMART_ATTRIBUTES,
-                g_param_spec_boxed ("drive-smart-attributes", NULL, NULL,
-                                    dbus_g_type_get_collection ("GPtrArray", SMART_DATA_STRUCT_TYPE),
+                PROP_DRIVE_ATA_SMART_ATTRIBUTES,
+                g_param_spec_boxed ("drive-ata-smart-attributes", NULL, NULL,
+                                    dbus_g_type_get_collection ("GPtrArray", ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE),
                                     G_PARAM_READABLE));
 
+
         g_object_class_install_property (
                 object_class,
                 PROP_LINUX_MD_COMPONENT_LEVEL,
@@ -1117,7 +1213,7 @@ devkit_disks_device_init (DevkitDisksDevice *device)
         device->priv->slaves_objpath = g_ptr_array_new ();
         device->priv->holders_objpath = g_ptr_array_new ();
 
-        device->priv->drive_smart_attributes = g_ptr_array_new ();
+        device->priv->drive_ata_smart_attributes = g_ptr_array_new ();
 }
 
 static void
@@ -1138,9 +1234,8 @@ devkit_disks_device_finalize (GObject *object)
 
         g_free (device->priv->native_path);
 
-        g_free (device->priv->drive_smart_last_self_test_result);
-        g_ptr_array_foreach (device->priv->drive_smart_attributes, (GFunc) g_value_array_free, NULL);
-        g_ptr_array_free (device->priv->drive_smart_attributes, TRUE);
+        g_ptr_array_foreach (device->priv->drive_ata_smart_attributes, (GFunc) g_value_array_free, NULL);
+        g_ptr_array_free (device->priv->drive_ata_smart_attributes, TRUE);
 
         for (l = device->priv->polling_inhibitors; l != NULL; l = l->next) {
                 DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
@@ -2532,32 +2627,23 @@ update_info_linux_md (DevkitDisksDevice *device)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* update drive_smart_* properties */
+/* update drive_ata_smart_* properties */
 static gboolean
-update_info_drive_smart (DevkitDisksDevice *device)
+update_info_drive_ata_smart (DevkitDisksDevice *device)
 {
-        /* Set whether device is S.M.A.R.T. capable
-         *
-         * TODO: need to check that it's hard disk and not e.g. an optical drive
-         *
-         * TODO: need to honor a quirk for certain USB drives being smart capable, cf.
-         *
-         *         Thanks to contributor Matthieu Castet, smartctl has
-         *         a new option '-d usbcypress'. So you can address
-         *         USB devices with cypress chips. The chipset
-         *         contains an ATACB proprietary pass through for ATA
-         *         commands passed through SCSI commands. Get current
-         *         version from CVS.
-         *
-         *       from http://smartmontools.sourceforge.net/
-         */
+        gboolean ata_smart_is_available;
 
-        /* drive_smart_is_capable */
-        if (device->priv->drive_connection_interface != NULL && g_str_has_prefix (device->priv->drive_connection_interface, "ata")) {
-                devkit_disks_device_set_drive_smart_is_capable (device, TRUE);
-        } else {
-                devkit_disks_device_set_drive_smart_is_capable (device, FALSE);
-        }
+        ata_smart_is_available = FALSE;
+        if (device->priv->device_is_drive &&
+            devkit_device_has_property (device->priv->d, "DKD_ATA_SMART_IS_AVAILABLE"))
+                ata_smart_is_available = devkit_device_get_property_as_boolean (device->priv->d, "DKD_ATA_SMART_IS_AVAILABLE");
+
+        devkit_disks_device_set_drive_ata_smart_is_available (device, ata_smart_is_available);
+
+        /* NOTE: we don't collect ATA SMART data here, we only set whether the device is ATA SMART capable;
+         *       collecting data is done in separate routines, see the
+         *       devkit_disks_device_drive_ata_smart_refresh_data() function for details.
+         */
 
         return TRUE;
 }
@@ -3064,8 +3150,8 @@ update_info (DevkitDisksDevice *device)
         if (!update_info_linux_md (device))
                 goto out;
 
-        /* drive_smart_* properties */
-        if (!update_info_drive_smart (device))
+        /* drive_ata_smart_* properties */
+        if (!update_info_drive_ata_smart (device))
                 goto out;
 
         /* device_is_system_internal property */
@@ -3304,6 +3390,14 @@ devkit_disks_device_new (DevkitDisksDaemon *daemon, DevkitDevice *d)
                 goto out;
         }
 
+        /* if just added, update the smart data if applicable */
+        if (device->priv->drive_ata_smart_is_available) {
+                gchar *ata_smart_refresh_data_options[] = {NULL};
+                devkit_disks_device_drive_ata_smart_refresh_data (device,
+                                                                  ata_smart_refresh_data_options,
+                                                                  NULL);
+        }
+
 out:
         return device;
 }
@@ -7135,357 +7229,470 @@ out:
 
 /*--------------------------------------------------------------------------------------------------------------*/
 
+static gchar *
+get_ata_smart_filename (DevkitDisksDevice *device)
+{
+        gchar *filename;
+
+        /* TODO: hmm... unique enough? Thinking serial number collisions.. but ata_id, scsi_id, usb_id in
+         * udev should take care of that...
+         */
+        filename = g_strdup_printf (PACKAGE_LOCALSTATE_DIR "/lib/DeviceKit-disks/ata-smart/%s-%s-%s-%s",
+                                    device->priv->drive_vendor,
+                                    device->priv->drive_model,
+                                    device->priv->drive_revision,
+                                    device->priv->drive_serial);
+
+        return filename;
+}
+
+static gboolean
+ata_smart_parse_attribute (const gchar *tokens, GValue *elem)
+{
+        gboolean ret;
+        guint id;
+        guint flags;
+        gboolean online, prefailure;
+        guint current;
+        gboolean current_valid;
+        guint worst;
+        gboolean worst_valid;
+        guint threshold;
+        gboolean threshold_valid;
+        gboolean good, good_valid;
+        guint pretty_unit;
+        guint64 pretty_value;
+        guint raw0, raw1, raw2, raw3, raw4, raw5;
+        gchar name[256];
+        GArray *raw_data;
+
+        ret = FALSE;
+
+        if (sscanf (tokens,
+                    "%d "                             /* id */
+                    "%s "                             /* name */
+                    "%d "                             /* flags */
+                    "%d %d "                          /* online, prefailure */
+                    "%d %d "                          /* current_value, current_value_valid */
+                    "%d %d "                          /* worst_value, worst_value_valid */
+                    "%d %d "                          /* threshold, threshold_valid */
+                    "%d %d "                          /* good, good_valid */
+                    "%d %" G_GUINT64_FORMAT " "       /* pretty_unit, pretty_value */
+                    "%02x %02x %02x %02x %02x %02x",  /* raw[6] */
+                    &id,
+                    name,
+                    &flags,
+                    &online, &prefailure,
+                    &current, &current_valid,
+                    &worst, &worst_valid,
+                    &threshold, &threshold_valid,
+                    &good, &good_valid,
+                    &pretty_unit, &pretty_value,
+                    &raw0, &raw1, &raw2, &raw3, &raw4, &raw5) != 21) {
+                goto out;
+        }
+
+        raw_data = g_array_new (FALSE, TRUE, sizeof (guchar));
+        g_array_append_val (raw_data, raw0);
+        g_array_append_val (raw_data, raw1);
+        g_array_append_val (raw_data, raw2);
+        g_array_append_val (raw_data, raw3);
+        g_array_append_val (raw_data, raw4);
+        g_array_append_val (raw_data, raw5);
+
+        g_value_init (elem, ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE);
+        g_value_take_boxed (elem, dbus_g_type_specialized_construct (ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE));
+        dbus_g_type_struct_set (elem,
+                                0, id,
+                                1, name,
+                                2, flags,
+                                3, online,
+                                4, prefailure,
+                                5, current,
+                                6, current_valid,
+                                7, worst,
+                                8, worst_valid,
+                                9, threshold,
+                                10, threshold_valid,
+                                11, good,
+                                12, good_valid,
+                                13, pretty_unit,
+                                14, pretty_value,
+                                15, raw_data,
+                                G_MAXUINT);
+
+        g_array_free (raw_data, TRUE);
+
+        ret = TRUE;
+
+ out:
+        return ret;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
 typedef struct {
         gboolean simulation;
-} DriveRefreshSmartDataData;
+} DriveRefreshAtaSmartDataData;
 
-static DriveRefreshSmartDataData *
-drive_smart_refresh_data_data_new (gboolean simulation)
+static DriveRefreshAtaSmartDataData *
+drive_ata_smart_refresh_data_data_new (gboolean simulation)
 {
-        DriveRefreshSmartDataData *data;
-        data = g_new0 (DriveRefreshSmartDataData, 1);
+        DriveRefreshAtaSmartDataData *data;
+        data = g_new0 (DriveRefreshAtaSmartDataData, 1);
         data->simulation = simulation;
         return data;
 }
 
 static void
-drive_smart_refresh_data_unref (DriveRefreshSmartDataData *data)
+drive_ata_smart_refresh_data_unref (DriveRefreshAtaSmartDataData *data)
 {
         g_free (data);
 }
 
 /* may be called with context==NULL */
 static void
-drive_smart_refresh_data_completed_cb (DBusGMethodInvocation *context,
-                                       DevkitDisksDevice *device,
-                                       PolKitCaller *pk_caller,
-                                       gboolean job_was_cancelled,
-                                       int status,
-                                       const char *stderr,
-                                       const char *stdout,
-                                       gpointer user_data)
-{
-        DriveRefreshSmartDataData *data = user_data;
-        int rc;
-        gboolean passed;
-        int n;
-        char **lines;
-        gboolean in_attributes;
-        int power_on_hours;
-        int temperature;
-        const char *last_self_test_result;
-        GTimeVal now;
-        gboolean attributes_has_upd;
+drive_ata_smart_refresh_data_completed_cb (DBusGMethodInvocation *context,
+                                           DevkitDisksDevice *device,
+                                           PolKitCaller *pk_caller,
+                                           gboolean job_was_cancelled,
+                                           int status,
+                                           const char *stderr,
+                                           const char *stdout,
+                                           gpointer user_data)
+{
+        DriveRefreshAtaSmartDataData *data;
+        gint rc;
+        gchar **tokens;
+        gboolean ata_smart_is_failing;
+        gboolean ata_smart_is_failing_valid;
+        gboolean ata_smart_has_bad_sectors;
+        gboolean ata_smart_has_bad_attributes;
+        gdouble ata_smart_temperature_kelvin;
+        guint64 ata_smart_power_on_seconds;
+        guint64 ata_smart_time_collected;
+        guint ata_smart_offline_data_collection_status;
+        guint ata_smart_offline_data_collection_seconds;
+        guint ata_smart_self_test_execution_status;
+        guint ata_smart_self_test_execution_percent_remaining;
+        gboolean ata_smart_short_and_extended_self_test_available;
+        gboolean ata_smart_conveyance_self_test_available;
+        gboolean ata_smart_start_self_test_available;
+        gboolean ata_smart_abort_self_test_available;
+        guint ata_smart_short_self_test_polling_minutes;
+        guint ata_smart_extended_self_test_polling_minutes;
+        guint ata_smart_conveyance_self_test_polling_minutes;
         GPtrArray *attributes;
+        guint n;
+
+        tokens = NULL;
+
+        data = user_data;
 
         if (job_was_cancelled || stdout == NULL) {
                 if (job_was_cancelled) {
+                        if (context != NULL)
+                                throw_error (context,
+                                             DEVKIT_DISKS_ERROR_CANCELLED,
+                                             "Job was cancelled");
+                } else {
+                        if (context != NULL)
+                                throw_error (context,
+                                             DEVKIT_DISKS_ERROR_FAILED,
+                                             "Error retrieving S.M.A.R.T. data: no output",
+                                             WEXITSTATUS (status), stderr);
+                }
+                goto out;
+        }
+
+        rc = WEXITSTATUS (status);
+
+        if (rc != 0) {
+                if (rc == 2) {
                         throw_error (context,
-                                     DEVKIT_DISKS_ERROR_CANCELLED,
-                                     "Job was cancelled");
+                                     DEVKIT_DISKS_ERROR_ATA_SMART_WOULD_WAKEUP,
+                                     "Error retrieving S.M.A.R.T. data: %s",
+                                     stderr);
                 } else {
                         throw_error (context,
                                      DEVKIT_DISKS_ERROR_FAILED,
-                                     "Error retrieving S.M.A.R.T. data: no output",
-                                     WEXITSTATUS (status), stderr);
+                                     "Error retrieving S.M.A.R.T. data: helper failed with exit code %d: %s",
+                                     rc, stderr);
                 }
                 goto out;
         }
 
-        rc = WEXITSTATUS (status);
+        g_print ("**************************************************\n");
+        g_print ("TODO: %s parse '%s'\n", device->priv->device_file, stdout);
+        g_print ("**************************************************\n");
 
-        if ((rc & (0x01|0x02|0x04)) != 0) {
-                /* update our setting if we "thought" (cf. update_info()) that this device
-                 * was S.M.A.R.T. capable
-                 */
-                if (device->priv->drive_smart_is_capable) {
-                        devkit_disks_device_set_drive_smart_is_capable (device, FALSE);
-                        drain_pending_changes (device, FALSE);
-                }
-                throw_error (context,
-                             DEVKIT_DISKS_ERROR_NOT_SMART_CAPABLE,
-                             "Device is not S.M.A.R.T. capable");
+        tokens = g_strsplit (stdout, "|", 0);
+
+        if (g_strv_length (tokens) < 4) {
+                if (context != NULL)
+                        throw_error (context,
+                                     DEVKIT_DISKS_ERROR_FAILED,
+                                     "malformed data '%s'", stdout);
                 goto out;
         }
 
-        /* TODO: is_enabled */
-        devkit_disks_device_set_drive_smart_is_enabled (device, TRUE);
+        if (sscanf (tokens[0],
+                    "%" G_GUINT64_FORMAT,
+                    &ata_smart_time_collected) != 1) {
+                if (context != NULL)
+                        throw_error (context,
+                                     DEVKIT_DISKS_ERROR_FAILED,
+                                     "error parsing section 0 '%s'", tokens[0]);
+                goto out;
+        }
 
-        g_get_current_time (&now);
-        devkit_disks_device_set_drive_smart_time_collected (device, now.tv_sec);
+        if (g_strcmp0 (tokens[1], "atasmartv0") != 0) {
+                if (context != NULL)
+                        throw_error (context,
+                                     DEVKIT_DISKS_ERROR_FAILED,
+                                     "unknown format '%s'", tokens[1]);
+                goto out;
+        }
 
+        if (sscanf (tokens[2],
+                    "%d %d %d %d %lg %" G_GUINT64_FORMAT,
+                    &ata_smart_is_failing,
+                    &ata_smart_is_failing_valid,
+                    &ata_smart_has_bad_sectors,
+                    &ata_smart_has_bad_attributes,
+                    &ata_smart_temperature_kelvin,
+                    &ata_smart_power_on_seconds) != 6) {
+                if (context != NULL)
+                        throw_error (context,
+                                     DEVKIT_DISKS_ERROR_FAILED,
+                                     "error parsing section 2 '%s'", tokens[2]);
+                goto out;
+        }
+
+        if (sscanf (tokens[3],
+                    "%d %d %d %d %d %d %d %d %d %d %d",
+                    &ata_smart_offline_data_collection_status,
+                    &ata_smart_offline_data_collection_seconds,
+                    &ata_smart_self_test_execution_status,
+                    &ata_smart_self_test_execution_percent_remaining,
+                    &ata_smart_short_and_extended_self_test_available,
+                    &ata_smart_conveyance_self_test_available,
+                    &ata_smart_start_self_test_available,
+                    &ata_smart_abort_self_test_available,
+                    &ata_smart_short_self_test_polling_minutes,
+                    &ata_smart_extended_self_test_polling_minutes,
+                    &ata_smart_conveyance_self_test_polling_minutes) != 11) {
+                if (context != NULL)
+                        throw_error (context,
+                                     DEVKIT_DISKS_ERROR_FAILED,
+                                     "error parsing section 3 '%s'", tokens[3]);
+                goto out;
+        }
+
+        devkit_disks_device_set_drive_ata_smart_is_failing (device, ata_smart_is_failing);
+        devkit_disks_device_set_drive_ata_smart_is_failing_valid (device, ata_smart_is_failing_valid);
+        devkit_disks_device_set_drive_ata_smart_has_bad_sectors (device, ata_smart_has_bad_sectors);
+        devkit_disks_device_set_drive_ata_smart_has_bad_attributes (device, ata_smart_has_bad_attributes);
+        devkit_disks_device_set_drive_ata_smart_time_collected (device, ata_smart_time_collected);
+        devkit_disks_device_set_drive_ata_smart_temperature_kelvin (device, ata_smart_temperature_kelvin);
+        devkit_disks_device_set_drive_ata_smart_power_on_seconds (device, ata_smart_power_on_seconds);
+        devkit_disks_device_set_drive_ata_smart_offline_data_collection_status (device, ata_smart_offline_data_collection_status);
+        devkit_disks_device_set_drive_ata_smart_offline_data_collection_seconds (device, ata_smart_offline_data_collection_seconds);
+        devkit_disks_device_set_drive_ata_smart_self_test_execution_status (device, ata_smart_self_test_execution_status);
+        devkit_disks_device_set_drive_ata_smart_self_test_execution_percent_remaining (device, ata_smart_self_test_execution_percent_remaining);
+        devkit_disks_device_set_drive_ata_smart_short_and_extended_self_test_available (device, ata_smart_short_and_extended_self_test_available);
+        devkit_disks_device_set_drive_ata_smart_conveyance_self_test_available (device, ata_smart_conveyance_self_test_available);
+        devkit_disks_device_set_drive_ata_smart_start_self_test_available (device, ata_smart_start_self_test_available);
+        devkit_disks_device_set_drive_ata_smart_abort_self_test_available (device, ata_smart_abort_self_test_available);
+        devkit_disks_device_set_drive_ata_smart_short_self_test_polling_minutes (device, ata_smart_short_self_test_polling_minutes);
+        devkit_disks_device_set_drive_ata_smart_extended_self_test_polling_minutes (device, ata_smart_extended_self_test_polling_minutes);
+        devkit_disks_device_set_drive_ata_smart_conveyance_self_test_polling_minutes (device, ata_smart_conveyance_self_test_polling_minutes);
+
+        /* then all attributes */
         attributes = g_ptr_array_new ();
+        for (n = 4; tokens[n] != NULL; n++) {
+                GValue elem = {0};
 
-        passed = TRUE;
-        power_on_hours = 0;
-        temperature = 0;
-        last_self_test_result = "";
-
-        if ((rc & 0x08) != 0) {
-                passed = FALSE;
-                devkit_disks_device_set_drive_smart_is_failing (device, TRUE);
-        }
-
-        lines = g_strsplit (stdout, "\n", 0);
-
-        in_attributes = FALSE;
-        attributes_has_upd = FALSE;
-        for (n = 0; lines[n] != NULL; n++) {
-                const char *line = (const char *) lines[n];
-                int id;
-                char name[256];
-                unsigned int flags;
-                int value;
-                int worst;
-                int threshold;
-                char type[256];
-                char updated[256];
-                char when_failed[256];
-                char raw_string_value[256];
-                int raw_value;
-                int self_test_execution_status;
-                gboolean parsed_attr;
-
-                /* We're looking at parsing this block of the output
-                 *
-                 * ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
-                 *   1 Raw_Read_Error_Rate     0x000f   200   200   051    Pre-fail  Always       -       1284
-                 *   3 Spin_Up_Time            0x0003   225   215   021    Pre-fail  Always       -       5725
-                 *   4 Start_Stop_Count        0x0032   100   100   000    Old_age   Always       -       204
-                 *   5 Reallocated_Sector_Ct   0x0033   199   199   140    Pre-fail  Always       -       2
-                 *   7 Seek_Error_Rate         0x000f   127   127   051    Pre-fail  Always       -       65877
-                 *   9 Power_On_Hours          0x0032   096   096   000    Old_age   Always       -       3429
-                 *  10 Spin_Retry_Count        0x0013   100   100   051    Pre-fail  Always       -       0
-                 *  11 Calibration_Retry_Count 0x0012   100   100   051    Old_age   Always       -       0
-                 *  12 Power_Cycle_Count       0x0032   100   100   000    Old_age   Always       -       153
-                 * 190 Temperature_Celsius     0x0022   058   032   045    Old_age   Always   In_the_past 42
-                 * 194 Temperature_Celsius     0x0022   253   253   000    Old_age   Always       -       43
-                 * 196 Reallocated_Event_Count 0x0032   198   198   000    Old_age   Always       -       2
-                 * 197 Current_Pending_Sector  0x0012   191   191   000    Old_age   Always       -       762
-                 * 198 Offline_Uncorrectable   0x0010   200   200   000    Old_age   Offline      -       21
-                 * 199 UDMA_CRC_Error_Count    0x003e   200   200   000    Old_age   Always       -       40
-                 * 200 Multi_Zone_Error_Rate   0x0009   170   170   051    Pre-fail  Offline      -       1542
-                 *
-                 */
-
-                if (data->simulation) {
-                        if (strstr (line, "self-assessment test result") != NULL) {
-                                if (strstr (line, "PASSED") != NULL)
-                                        passed = TRUE;
-                                else
-                                        passed = FALSE;
-                        }
+                if (!ata_smart_parse_attribute (tokens[n], &elem)) {
+                        if (context != NULL)
+                                throw_error (context,
+                                             DEVKIT_DISKS_ERROR_FAILED,
+                                             "error parsing section %d '%s'", n, tokens[n]);
+                        g_ptr_array_foreach (attributes, (GFunc) g_value_array_free, NULL);
+                        g_ptr_array_free (attributes, TRUE);
+                        goto out;
                 }
 
-                if (g_str_has_prefix (line, "ID# ATTRIBUTE_NAME ")) {
-                        in_attributes = TRUE;
-                        if (strstr (line, "UPDATED") != NULL)
-                                attributes_has_upd = TRUE;
-                        continue;
-                }
+                g_ptr_array_add (attributes, g_value_get_boxed (&elem));
+        }
 
-                if (strlen (line) >= 256) {
-                        g_warning ("Ignoring line '%s' (too long)", line);
-                        continue;
-                }
+        devkit_disks_device_set_drive_ata_smart_attributes_steal (device, attributes);
 
-                if (!in_attributes) {
-                        if (sscanf (line, "Self-test execution status: ( %d)",
-                                    &self_test_execution_status) == 1) {
-                                //g_warning ("self_test_execution_status: %d", self_test_execution_status);
+        /* emit change event since we've updated the smart data */
+        drain_pending_changes (device, FALSE);
 
-                                switch (self_test_execution_status >> 4) {
-                                case 0:
-                                        last_self_test_result = "completed_ok";
-                                        break;
-                                case 1:
-                                        last_self_test_result = "not_completed_aborted";
-                                        break;
-                                case 2:
-                                        last_self_test_result = "not_completed_aborted_reset";
-                                        break;
-                                case 3:
-                                        last_self_test_result = "not_completed_unknown_reason";
-                                        break;
-                                case 4:
-                                        last_self_test_result = "completed_failed_unknown_reason";
-                                        break;
-                                case 5:
-                                        last_self_test_result = "completed_failed_electrical";
-                                        break;
-                                case 6:
-                                        last_self_test_result = "completed_failed_servo";
-                                        break;
-                                case 7:
-                                        last_self_test_result = "completed_failed_read";
-                                        break;
-                                case 8:
-                                        last_self_test_result = "completed_failed_damage";
-                                        break;
-                                default:
-                                        last_self_test_result = "unknown";
-                                        break;
-                                }
-                                devkit_disks_device_set_drive_smart_last_self_test_result (device, last_self_test_result);
+        /* if not simulating, store the retrieved data and do some house-keeping as well
+         *
+         * TODO: it's probably somewhat inefficient to do house-keeping like this (better
+         *       to just append to the file and do housekeeping only on startup and every
+         *       24 hours or so)
+         *
+         * TODO: retrieving/storing the data should probably be async as well
+         */
+        if (!data->simulation) {
+                gchar *filename;
+                gchar *contents;
+                gsize length;
+                GError *error;
+                GString *s;
+                time_t now;
+
+                filename = get_ata_smart_filename (device);
+                if (filename == NULL) {
+                        if (context != NULL)
+                                throw_error (context,
+                                             DEVKIT_DISKS_ERROR_FAILED,
+                                             "Error computing smart data filename for device");
+                        goto out;
+                }
 
+                error = NULL;
+                if (!g_file_get_contents (filename,
+                                          &contents,
+                                          &length,
+                                          &error)) {
+                        if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) {
+                                /* it's ok if the file doesn't exist */
+                                g_error_free (error);
+                        } else {
+                                if (context != NULL)
+                                        throw_error (context,
+                                                     DEVKIT_DISKS_ERROR_FAILED,
+                                                     "Error retrieving existing SMART data from %s: %s", filename, error->message);
+                                g_error_free (error);
+                                g_free (filename);
+                                goto out;
                         }
-                        continue;
                 }
 
-                if (strlen (line) == 0) {
-                        break;
-                }
+                now = time (NULL);
 
-                parsed_attr = FALSE;
-                memset (raw_string_value, '\0', sizeof raw_string_value);
-                if (attributes_has_upd) {
-                        parsed_attr = sscanf (line, "%d %s 0x%x %d %d %d %s %s %s %255c",
-                                              &id, name, &flags, &value, &worst, &threshold,
-                                              type, updated, when_failed, raw_string_value) == 10;
-                } else {
-                        parsed_attr = sscanf (line, "%d %s 0x%x %d %d %d %s %s %255c",
-                                              &id, name, &flags, &value, &worst, &threshold,
-                                              type, when_failed, raw_string_value) == 9;
-                }
-
-                if (parsed_attr) {
-                        /*
-                        g_printerr ("                id=%d\n", id);
-                        g_printerr ("              name='%s'\n", name);
-                        g_printerr ("             flags=0x%x\n", flags);
-                        g_printerr ("             value=%d\n", value);
-                        g_printerr ("             worst=%d\n", worst);
-                        g_printerr ("         threshold=%d\n", threshold);
-                        g_printerr ("              type='%s'\n", type);
-                        g_printerr ("           updated='%s'\n", updated);
-                        g_printerr ("       when_failed='%s'\n", when_failed);
-                        g_printerr ("  raw_string_value='%s'\n", raw_string_value);
-                        */
-
-                        if (sscanf (raw_string_value, "%d", &raw_value) == 1) {
-                                if (id == 9) {
-                                        power_on_hours = raw_value;
-                                        devkit_disks_device_set_drive_smart_time_powered_on (device, raw_value * 3600);
-                                } else if (id == 194) {
-                                        temperature = raw_value;
-                                        devkit_disks_device_set_drive_smart_temperature (device, raw_value);
-                                }
-                        }
+                s = g_string_new (NULL);
+                if (contents != NULL) {
+                        gchar **lines;
 
-                        if (id == 197) {
-                        }
+                        lines = g_strsplit (contents, "\n", 0);
+                        for (n = 0; lines[n] != NULL; n++) {
+                                time_t time_collected;
 
-                        GValue elem = {0};
-                        g_value_init (&elem, SMART_DATA_STRUCT_TYPE);
-                        g_value_take_boxed (&elem, dbus_g_type_specialized_construct (SMART_DATA_STRUCT_TYPE));
-                        dbus_g_type_struct_set (&elem,
-                                                0, id,
-                                                1, name,
-                                                2, flags,
-                                                3, value,
-                                                4, worst,
-                                                5, threshold,
-                                                6, raw_string_value,
-                                                G_MAXUINT);
-                        g_ptr_array_add (attributes, g_value_get_boxed (&elem));
+                                time_collected = (time_t) atoll (lines[n]);
 
-                }
+                                /* keep data around for 7 days */
+                                if (now - time_collected > 7 * 24 * 60 * 60)
+                                        continue;
 
-        }
-        g_strfreev (lines);
+                                g_string_append (s, lines[n]);
+                                g_string_append_c (s, '\n');
+                        }
+                        g_strfreev (lines);
 
-        devkit_disks_device_set_drive_smart_is_failing (device, !passed);
+                        g_free (contents);
+                }
 
-        /* this function steals the attributes */
-        devkit_disks_device_set_drive_smart_attributes_steal (device, attributes);
+                g_string_append (s, stdout);
 
-        /* emit change event since we've updated the smart data */
-        drain_pending_changes (device, FALSE);
+                error = NULL;
+                if (!g_file_set_contents (filename,
+                                          s->str,
+                                          s->len,
+                                          &error)) {
+                        if (context != NULL)
+                                throw_error (context,
+                                             DEVKIT_DISKS_ERROR_FAILED,
+                                             "Error retrieving saving SMART data to %s: %s", filename, error->message);
+                        g_error_free (error);
+                        g_free (filename);
+                        goto out;
+                }
 
-        /* add result to database */
-        if (!data->simulation) {
-                devkit_disks_logger_record_smart_values (devkit_disks_daemon_local_get_logger (device->priv->daemon),
-                                                         device);
+                g_free (filename);
         }
 
         if (context != NULL)
-                dbus_g_method_return (context, passed, power_on_hours, temperature, last_self_test_result);
+                dbus_g_method_return (context);
 out:
-        ;
+        g_strfreev (tokens);
 }
 
 /* may be called with context==NULL */
 gboolean
-devkit_disks_device_drive_smart_refresh_data (DevkitDisksDevice     *device,
-                                              char                 **options,
-                                              DBusGMethodInvocation *context)
+devkit_disks_device_drive_ata_smart_refresh_data (DevkitDisksDevice     *device,
+                                                  char                 **options,
+                                                  DBusGMethodInvocation *context)
 {
         int n;
         char *argv[10];
         GError *error;
         PolKitCaller *pk_caller;
-        const char *simulpath;
+        const char *simuldata;
         gboolean nowakeup;
+        uid_t caller_uid;
 
         pk_caller = NULL;
         if (context != NULL) {
-                if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon,
-                                                                                  context)) == NULL)
+                if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon, context)) == NULL)
                         goto out;
         }
 
-        if (!device->priv->device_is_drive) {
-                throw_error (context, DEVKIT_DISKS_ERROR_NOT_DRIVE,
-                             "Device is not a drive");
-                goto out;
-        }
+        if (pk_caller != NULL)
+                polkit_caller_get_uid (pk_caller, &caller_uid);
+        else
+                caller_uid = 0;
 
-        if (!device->priv->drive_smart_is_capable) {
+        if (!device->priv->drive_ata_smart_is_available) {
                 throw_error (context,
-                             DEVKIT_DISKS_ERROR_NOT_SMART_CAPABLE,
-                             "Device is not S.M.A.R.T. capable");
+                             DEVKIT_DISKS_ERROR_ATA_SMART_NOT_AVAILABLE,
+                             "Device does not support ATA SMART");
                 goto out;
         }
 
-        if (context != NULL) {
+        if (context != NULL && caller_uid != 0) {
                 if (!devkit_disks_damon_local_check_auth (device->priv->daemon,
                                                           pk_caller,
-                                                          "org.freedesktop.devicekit.disks.drive-smart-refresh",
+                                                          "org.freedesktop.devicekit.disks.drive-ata-smart-refresh",
                                                           context)) {
                         goto out;
                 }
         }
 
-        simulpath = NULL;
+        simuldata = NULL;
         nowakeup = FALSE;
         for (n = 0; options[n] != NULL; n++) {
                 if (g_str_has_prefix (options[n], "simulate=")) {
                         if (context != NULL) {
-                                uid_t uid;
-                                if (!polkit_caller_get_uid (pk_caller, &uid) || uid != 0) {
+                                if (caller_uid != 0) {
                                         throw_error (context,
                                                      DEVKIT_DISKS_ERROR_FAILED,
                                                      "Only uid 0 may use the simulate= option");
                                         goto out;
                                 }
                         }
-                        simulpath = (const char *) options[n] + 9;
+                        simuldata = (const char *) options[n] + 9;
                 } else if (strcmp (options[n], "nowakeup") == 0) {
                         nowakeup = TRUE;
                 }
         }
 
-        if (simulpath != NULL) {
+        if (simuldata != NULL) {
                 n = 0;
-                argv[n++] = "cat";
-                argv[n++] = (char *) simulpath;
+                argv[n++] = "echo";
+                argv[n++] = (char *) simuldata;
                 argv[n++] = NULL;
         } else {
                 n = 0;
-                /* TODO: honor option 'nowakeup' */
-                argv[n++] = "smartctl";
-                argv[n++] = "--all";
+                argv[n++] = PACKAGE_LIBEXEC_DIR "/devkit-disks-helper-ata-smart-collect";
                 argv[n++] = device->priv->device_file;
+                argv[n++] = nowakeup ? "1" : "0";
                 argv[n++] = NULL;
         }
 
@@ -7498,9 +7705,9 @@ devkit_disks_device_drive_smart_refresh_data (DevkitDisksDevice     *device,
                       pk_caller,
                       argv,
                       NULL,
-                      drive_smart_refresh_data_completed_cb,
-                      drive_smart_refresh_data_data_new (simulpath != NULL),
-                      (GDestroyNotify) drive_smart_refresh_data_unref)) {
+                      drive_ata_smart_refresh_data_completed_cb,
+                      drive_ata_smart_refresh_data_data_new (simuldata != NULL),
+                      (GDestroyNotify) drive_ata_smart_refresh_data_unref)) {
                 goto out;
         }
 
@@ -7512,16 +7719,201 @@ out:
 
 /*--------------------------------------------------------------------------------------------------------------*/
 
+gboolean
+devkit_disks_device_drive_ata_smart_get_historical_data (DevkitDisksDevice     *device,
+                                                         guint64                from,
+                                                         guint64                to,
+                                                         DBusGMethodInvocation *context)
+{
+        PolKitCaller *pk_caller;
+        GPtrArray *array;
+        gchar *filename;
+        gchar *contents;
+        GError *error;
+
+        pk_caller = NULL;
+        filename = NULL;
+
+        if (context != NULL) {
+                if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon,
+                                                                                  context)) == NULL)
+                        goto out;
+        }
+
+        if (!device->priv->drive_ata_smart_is_available) {
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_ATA_SMART_NOT_AVAILABLE,
+                             "Device does not support ATA SMART");
+                goto out;
+        }
+
+        if (context != NULL) {
+                if (!devkit_disks_damon_local_check_auth (
+                            device->priv->daemon,
+                            pk_caller,
+                            "org.freedesktop.devicekit.disks.drive-ata-smart-retrieve-historical-data",
+                            context)) {
+                        goto out;
+                }
+        }
+
+        if (from > to) {
+                throw_error (context, DEVKIT_DISKS_ERROR_FAILED, "Malformed time range (from > to)");
+                goto out;
+        }
+
+        to = time (NULL);
+
+        filename = get_ata_smart_filename (device);
+        if (filename == NULL) {
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_FAILED,
+                             "Error computing smart data filename for device");
+                goto out;
+        }
+
+        error = NULL;
+        if (!g_file_get_contents (filename,
+                                  &contents,
+                                  NULL,
+                                  &error)) {
+                if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) {
+                        /* it's ok if the file doesn't exist */
+                        g_error_free (error);
+                } else {
+                        throw_error (context,
+                                     DEVKIT_DISKS_ERROR_FAILED,
+                                     "Error retrieving existing SMART data from %s: %s", filename, error->message);
+                        g_error_free (error);
+                        g_free (filename);
+                        goto out;
+                }
+        }
+
+        array = g_ptr_array_new ();
+
+        if (contents != NULL) {
+                gchar **lines;
+                guint n;
+
+                lines = g_strsplit (contents, "\n", 0);
+                for (n = 0; lines[n] != NULL; n++) {
+                        gboolean ata_smart_is_failing;
+                        gboolean ata_smart_is_failing_valid;
+                        gboolean ata_smart_has_bad_sectors;
+                        gboolean ata_smart_has_bad_attributes;
+                        gdouble ata_smart_temperature_kelvin;
+                        guint64 ata_smart_power_on_seconds;
+                        guint64 ata_smart_time_collected;
+                        gchar **tokens;
+                        GPtrArray *attributes;
+                        guint m;
+                        GValue sample_elem = {0};
+
+                        if (strlen (lines[n]) == 0)
+                                continue;
+
+                        tokens = g_strsplit (lines[n], "|", 0);
+
+                        if (sscanf (tokens[0],
+                                    "%" G_GUINT64_FORMAT,
+                                    &ata_smart_time_collected) != 1) {
+                                g_warning ("error parsing section 0 '%s'", tokens[0]);
+                                g_strfreev (tokens);
+                                continue;
+                        }
+
+                        if (! ((ata_smart_time_collected >= from) && (ata_smart_time_collected <= to))) {
+                                g_strfreev (tokens);
+                                continue;
+                        }
+
+                        if (g_strcmp0 (tokens[1], "atasmartv0") != 0) {
+                                g_warning ("unknown format '%s'", tokens[1]);
+                                g_strfreev (tokens);
+                                continue;
+                        }
+
+                        if (sscanf (tokens[2],
+                                    "%d %d %d %d %lg %" G_GUINT64_FORMAT,
+                                    &ata_smart_is_failing,
+                                    &ata_smart_is_failing_valid,
+                                    &ata_smart_has_bad_sectors,
+                                    &ata_smart_has_bad_attributes,
+                                    &ata_smart_temperature_kelvin,
+                                    &ata_smart_power_on_seconds) != 6) {
+                                g_warning ("error parsing section 2 '%s'", tokens[2]);
+                                g_strfreev (tokens);
+                                continue;
+                        }
+
+                        attributes = g_ptr_array_new ();
+                        for (m = 4; tokens[m] != NULL; m++) {
+                                GValue elem = {0};
+
+                                /* ignore errors... */
+                                if (!ata_smart_parse_attribute (tokens[m], &elem)) {
+                                        g_warning ("error parsing '%s' in historical data", tokens[m]);
+                                        continue;
+                                }
+
+                                g_ptr_array_add (attributes, g_value_get_boxed (&elem));
+                        }
+
+                        g_value_init (&sample_elem, ATA_SMART_HISTORICAL_SMART_DATA_STRUCT_TYPE);
+                        g_value_take_boxed (&sample_elem, dbus_g_type_specialized_construct (ATA_SMART_HISTORICAL_SMART_DATA_STRUCT_TYPE));
+                        dbus_g_type_struct_set (&sample_elem,
+                                                0, ata_smart_time_collected,
+                                                1, ata_smart_is_failing,
+                                                2, ata_smart_is_failing_valid,
+                                                3, ata_smart_has_bad_sectors,
+                                                4, ata_smart_has_bad_attributes,
+                                                5, ata_smart_temperature_kelvin,
+                                                6, ata_smart_power_on_seconds,
+                                                7, attributes,
+                                                G_MAXUINT);
+                        g_ptr_array_add (array, g_value_get_boxed (&sample_elem));
+
+                        g_ptr_array_foreach (attributes, (GFunc) g_value_array_free, NULL);
+                        g_ptr_array_free (attributes, TRUE);
+
+                        g_strfreev (tokens);
+                }
+                g_strfreev (lines);
+
+                g_free (contents);
+        }
+
+
+        dbus_g_method_return (context, array);
+
+        g_ptr_array_foreach (array, (GFunc) g_value_array_free, NULL);
+        g_ptr_array_free (array, TRUE);
+
+out:
+        g_free (filename);
+        if (pk_caller != NULL)
+                polkit_caller_unref (pk_caller);
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
 static void
-drive_smart_initiate_selftest_completed_cb (DBusGMethodInvocation *context,
-                                            DevkitDisksDevice *device,
-                                            PolKitCaller *pk_caller,
-                                            gboolean job_was_cancelled,
-                                            int status,
-                                            const char *stderr,
-                                            const char *stdout,
-                                            gpointer user_data)
+drive_ata_smart_initiate_selftest_completed_cb (DBusGMethodInvocation *context,
+                                                DevkitDisksDevice *device,
+                                                PolKitCaller *pk_caller,
+                                                gboolean job_was_cancelled,
+                                                int status,
+                                                const char *stderr,
+                                                const char *stdout,
+                                                gpointer user_data)
 {
+        char *options[] = {NULL};
+
+        /* no matter what happened, refresh the data */
+        devkit_disks_device_drive_ata_smart_refresh_data (device, options, NULL);
+
         if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
 
                 dbus_g_method_return (context);
@@ -7541,10 +7933,10 @@ drive_smart_initiate_selftest_completed_cb (DBusGMethodInvocation *context,
 }
 
 gboolean
-devkit_disks_device_drive_smart_initiate_selftest (DevkitDisksDevice     *device,
-                                                   const char            *test,
-                                                   gboolean               captive,
-                                                   DBusGMethodInvocation *context)
+devkit_disks_device_drive_ata_smart_initiate_selftest (DevkitDisksDevice     *device,
+                                                       const char            *test,
+                                                       gchar                **options,
+                                                       DBusGMethodInvocation *context)
 {
         int n;
         char *argv[10];
@@ -7554,49 +7946,35 @@ devkit_disks_device_drive_smart_initiate_selftest (DevkitDisksDevice     *device
         if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon, context)) == NULL)
                 goto out;
 
-        if (!device->priv->device_is_drive) {
-                throw_error (context, DEVKIT_DISKS_ERROR_NOT_DRIVE,
-                             "Device is not a drive");
+        if (!device->priv->drive_ata_smart_is_available) {
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_ATA_SMART_NOT_AVAILABLE,
+                             "Device does not support ATA SMART");
                 goto out;
         }
 
-        if (captive) {
-                if (devkit_disks_device_local_is_busy (device)) {
-                        throw_error (context, DEVKIT_DISKS_ERROR_BUSY,
-                                     "Device is busy");
-                        goto out;
-                }
-
-                if (devkit_disks_device_local_partitions_are_busy (device)) {
-                        throw_error (context, DEVKIT_DISKS_ERROR_BUSY,
-                                     "A partition on the device is busy");
-                        goto out;
-                }
-        }
-
         if (!devkit_disks_damon_local_check_auth (device->priv->daemon,
                                                   pk_caller,
-                                                  "org.freedesktop.devicekit.disks.drive-smart-selftest",
+                                                  "org.freedesktop.devicekit.disks.drive-ata-smart-selftest",
                                                   context)) {
                 goto out;
         }
 
         n = 0;
-        argv[n++] = PACKAGE_LIBEXEC_DIR "/devkit-disks-helper-smart-selftest";
+        argv[n++] = PACKAGE_LIBEXEC_DIR "/devkit-disks-helper-ata-smart-selftest";
         argv[n++] = device->priv->device_file;
         argv[n++] = (char *) test;
-        argv[n++] = captive ? "1" : "0";
         argv[n++] = NULL;
 
         error = NULL;
         if (!job_new (context,
-                      "DriveSmartInitiateSelftest",
+                      "DriveAtaSmartInitiateSelftest",
                       TRUE,
                       device,
                       pk_caller,
                       argv,
                       NULL,
-                      drive_smart_initiate_selftest_completed_cb,
+                      drive_ata_smart_initiate_selftest_completed_cb,
                       NULL,
                       NULL)) {
                 goto out;
index 739ed1f..667870c 100644 (file)
@@ -148,19 +148,19 @@ gboolean devkit_disks_device_filesystem_set_label (DevkitDisksDevice     *device
                                                    const char            *new_label,
                                                    DBusGMethodInvocation *context);
 
-gboolean devkit_disks_device_drive_smart_refresh_data (DevkitDisksDevice     *device,
-                                                       char                 **options,
-                                                       DBusGMethodInvocation *context);
-
-gboolean devkit_disks_device_drive_smart_get_historical_data (DevkitDisksDevice     *device,
-                                                              guint64                from,
-                                                              guint64                to,
-                                                              DBusGMethodInvocation *context);
-
-gboolean devkit_disks_device_drive_smart_initiate_selftest (DevkitDisksDevice     *device,
-                                                            const char            *test,
-                                                            gboolean               captive,
-                                                            DBusGMethodInvocation *context);
+gboolean devkit_disks_device_drive_ata_smart_refresh_data (DevkitDisksDevice     *device,
+                                                           char                 **options,
+                                                           DBusGMethodInvocation *context);
+
+gboolean devkit_disks_device_drive_ata_smart_get_historical_data (DevkitDisksDevice     *device,
+                                                                  guint64                from,
+                                                                  guint64                to,
+                                                                  DBusGMethodInvocation *context);
+
+gboolean devkit_disks_device_drive_ata_smart_initiate_selftest (DevkitDisksDevice     *device,
+                                                                const char            *test,
+                                                                char                 **options,
+                                                                DBusGMethodInvocation *context);
 
 gboolean devkit_disks_device_linux_md_stop (DevkitDisksDevice     *device,
                                             char                 **options,
diff --git a/src/devkit-disks-logger.c b/src/devkit-disks-logger.c
deleted file mode 100644 (file)
index 3c88703..0000000
+++ /dev/null
@@ -1,508 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <glib/gi18n-lib.h>
-#include <glib-object.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <sqlite3.h>
-
-#include "devkit-disks-daemon.h"
-#include "devkit-disks-device.h"
-#include "devkit-disks-device-private.h"
-#include "devkit-disks-logger.h"
-
-struct DevkitDisksLoggerPrivate
-{
-        sqlite3 *db;
-};
-
-static GObjectClass *parent_class = NULL;
-
-G_DEFINE_TYPE (DevkitDisksLogger, devkit_disks_logger, G_TYPE_OBJECT)
-
-#define DEVKIT_DISKS_LOGGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEVKIT_DISKS_TYPE_DEVICE, DevkitDisksDevicePrivate))
-
-
-static void
-devkit_disks_logger_finalize (DevkitDisksLogger *logger)
-{
-        if (logger->priv->db != NULL)
-                sqlite3_close (logger->priv->db);
-
-        if (G_OBJECT_CLASS (parent_class)->finalize)
-                (* G_OBJECT_CLASS (parent_class)->finalize) (G_OBJECT (logger));
-}
-
-static void
-devkit_disks_logger_class_init (DevkitDisksLoggerClass *klass)
-{
-        GObjectClass *obj_class = (GObjectClass *) klass;
-
-        parent_class = g_type_class_peek_parent (klass);
-
-        obj_class->finalize = (GObjectFinalizeFunc) devkit_disks_logger_finalize;
-
-}
-
-static void
-devkit_disks_logger_init (DevkitDisksLogger *logger)
-{
-        logger->priv = g_new0 (DevkitDisksLoggerPrivate, 1);
-}
-
-DevkitDisksLogger *
-devkit_disks_logger_new (void)
-{
-        int ret;
-        char *err_msg;
-        DevkitDisksLogger * logger;
-
-        logger = DEVKIT_DISKS_LOGGER (g_object_new (DEVKIT_DISKS_TYPE_LOGGER, NULL));
-
-        ret = sqlite3_open_v2 (PACKAGE_LOCALSTATE_DIR "/lib/DeviceKit-disks/db.sqlite3",
-                               &logger->priv->db,
-                               SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
-                               NULL);
-        if (ret != SQLITE_OK) {
-                g_warning ("error opening db: %s", sqlite3_errmsg (logger->priv->db));
-                sqlite3_close (logger->priv->db);
-                goto error;
-        }
-
-        /* create tables */
-        ret = sqlite3_exec (logger->priv->db,
-                            "CREATE TABLE SmartEntry ("
-                            "  smart_entry_id INTEGER PRIMARY KEY, "
-                            "  disk_id TEXT, "
-                            "  time_collected INTEGER, "
-                            "  temperature INTEGER, "
-                            "  time_powered_on INTEGER, "
-                            "  last_self_test_result TEXT, "
-                            "  is_failing INTEGER"
-                            ");"
-                            "CREATE TABLE SmartAttr ("
-                            "  smart_entry_id INTEGER, "
-                            "  disk_id TEXT, "
-                            "  time_collected INTEGER, "
-                            "  id INTEGER, "
-                            "  name TEXT, "
-                            "  flags INTEGER, "
-                            "  value INTEGER, "
-                            "  worst INTEGER, "
-                            "  threshold INTEGER, "
-                            "  raw TEXT"
-                            ");",
-                            NULL,
-                            NULL,
-                            &err_msg);
-        if (ret != SQLITE_OK) {
-                g_warning ("SQL error: %s", err_msg);
-                sqlite3_free (err_msg);
-        }
-
-        return logger;
-
-error:
-        g_object_unref (logger);
-        return NULL;
-}
-
-static char *
-drive_get_safe_uuid (DevkitDisksDevice *device)
-{
-        char *s;
-        char *result;
-
-        result = NULL;
-
-        if (device->priv->drive_vendor == NULL || strlen (device->priv->drive_vendor) == 0)
-                goto out;
-        if (device->priv->drive_model == NULL || strlen (device->priv->drive_model) == 0)
-                goto out;
-        if (device->priv->drive_revision == NULL || strlen (device->priv->drive_revision) == 0)
-                goto out;
-        if (device->priv->drive_serial == NULL || strlen (device->priv->drive_serial) == 0)
-                goto out;
-
-        s = g_strdup_printf ("%s_%s_%s_%s",
-                             device->priv->drive_vendor,
-                             device->priv->drive_model,
-                             device->priv->drive_revision,
-                             device->priv->drive_serial);
-        result = g_uri_escape_string (s, NULL, FALSE);
-        g_free (s);
-
-out:
-        return result;
-}
-
-void
-devkit_disks_logger_record_smart_values (DevkitDisksLogger *logger,
-                                         DevkitDisksDevice *device)
-{
-        int n;
-        int ret;
-        char *err_msg;
-        char *s;
-        char *disk_id;
-        sqlite3_int64 row_id;
-        GString *str;
-
-        g_return_if_fail (device != NULL);
-        g_return_if_fail (logger != NULL);
-        g_return_if_fail (logger->priv->db != NULL);
-
-        disk_id = NULL;
-
-        disk_id = drive_get_safe_uuid (device);
-        if (disk_id == NULL) {
-                g_warning ("no drive uuid for %s", device->priv->native_path);
-                goto out;
-        }
-
-        s = sqlite3_mprintf (
-                "BEGIN TRANSACTION;"
-                "INSERT INTO SmartEntry "
-                "(disk_id, time_collected, temperature, time_powered_on, last_self_test_result, is_failing) "
-                "VALUES ('%q', %" G_GUINT64_FORMAT ", %d, %" G_GUINT64_FORMAT ", '%q', %d)",
-                disk_id,
-                device->priv->drive_smart_time_collected,
-                (int) device->priv->drive_smart_temperature,
-                device->priv->drive_smart_time_powered_on,
-                device->priv->drive_smart_last_self_test_result,
-                device->priv->drive_smart_is_failing ? 1 : 0);
-        ret = sqlite3_exec (logger->priv->db, s, NULL, NULL, &err_msg);
-        sqlite3_free (s);
-        if (ret != SQLITE_OK) {
-                g_warning ("SQL error: %s", err_msg);
-                sqlite3_free (err_msg);
-                goto out;
-        }
-
-        row_id = sqlite3_last_insert_rowid (logger->priv->db);
-
-        str = g_string_new (NULL);
-        for (n = 0; n < (int) device->priv->drive_smart_attributes->len; n++) {
-                GValue elem = {0};
-                int id;
-                char *name;
-                int flags;
-                int value;
-                int worst;
-                int threshold;
-                char *raw_string;
-
-                g_value_init (&elem, SMART_DATA_STRUCT_TYPE);
-                g_value_set_static_boxed (&elem, device->priv->drive_smart_attributes->pdata[n]);
-                dbus_g_type_struct_get (&elem,
-                                        0, &id,
-                                        1, &name,
-                                        2, &flags,
-                                        3, &value,
-                                        4, &worst,
-                                        5, &threshold,
-                                        6, &raw_string,
-                                        G_MAXUINT);
-
-                s = sqlite3_mprintf (
-                        "INSERT INTO SmartAttr "
-                        "VALUES (%" G_GINT64_FORMAT ", '%q', %" G_GUINT64_FORMAT ", %d, '%q', %d, %d, %d, %d, '%q');\n",
-                        row_id,
-                        disk_id,
-                        device->priv->drive_smart_time_collected,
-                        id,
-                        name,
-                        flags,
-                        value,
-                        worst,
-                        threshold,
-                        raw_string);
-                g_string_append (str, s);
-                sqlite3_free (s);
-        }
-
-        g_string_append_printf (str, "COMMIT;");
-
-        s = g_string_free (str, FALSE);
-        ret = sqlite3_exec (logger->priv->db, s, NULL, NULL, &err_msg);
-        g_free (s);
-        if (ret != SQLITE_OK) {
-                g_warning ("SQL error: %s", err_msg);
-                sqlite3_free (err_msg);
-        }
-out:
-        g_free (disk_id);
-}
-
-static gboolean
-throw_error (DBusGMethodInvocation *context, int error_code, const char *format, ...)
-{
-        GError *error;
-        va_list args;
-        char *message;
-
-        if (context == NULL)
-                return TRUE;
-
-        va_start (args, format);
-        message = g_strdup_vprintf (format, args);
-        va_end (args);
-
-        error = g_error_new (DEVKIT_DISKS_ERROR,
-                             error_code,
-                             "%s", message);
-        dbus_g_method_return_error (context, error);
-        g_error_free (error);
-        g_free (message);
-        return TRUE;
-}
-
-typedef struct {
-        GPtrArray *array;
-
-        gint64 cur_rowid;
-        gboolean needs_draining;
-
-        guint64 time_collected;
-        double temperature;
-        guint64 time_powered_on;
-        char last_self_test_result[256];
-        gboolean is_failing;
-        GPtrArray *attrs;
-} HistoricalData;
-
-static void
-historical_data_drain (HistoricalData *data)
-{
-        GValue elem = {0};
-
-        if (!data->needs_draining)
-                return;
-
-        g_value_init (&elem, HISTORICAL_SMART_DATA_STRUCT_TYPE);
-        g_value_take_boxed (&elem, dbus_g_type_specialized_construct (HISTORICAL_SMART_DATA_STRUCT_TYPE));
-        dbus_g_type_struct_set (&elem,
-                                0, data->time_collected,
-                                1, data->temperature,
-                                2, data->time_powered_on,
-                                3, data->last_self_test_result,
-                                4, data->is_failing,
-                                5, data->attrs,
-                                G_MAXUINT);
-        g_ptr_array_add (data->array, g_value_get_boxed (&elem));
-
-        g_ptr_array_foreach (data->attrs, (GFunc) g_value_array_free, NULL);
-        g_ptr_array_free (data->attrs, TRUE);
-        data->attrs = NULL;
-        data->needs_draining = FALSE;
-}
-
-static int
-historical_data_cb (void *user_data, int argc, char **argv, char **col_name)
-{
-        HistoricalData *data = (HistoricalData *) user_data;
-        gint64 rowid;
-        int id;
-        const char *name;
-        int flags;
-        int value;
-        int worst;
-        int threshold;
-        const char *raw;
-
-        if (argc != 13) {
-                g_warning ("expected 13 columns, got %d instead", argc);
-                goto out;
-        }
-
-        /* TODO: could add checks for the column types */
-
-        rowid = atoll (argv[0]);
-        if (rowid != data->cur_rowid) {
-                if (data->needs_draining) {
-                        historical_data_drain (data);
-                }
-
-                data->needs_draining = TRUE;
-                data->cur_rowid = rowid;
-                data->time_collected = atoll (argv[1]);
-                data->temperature = atof (argv[2]);
-                data->time_powered_on = atoll (argv[3]);
-                strncpy (data->last_self_test_result, argv[4], 256);
-                data->is_failing = (strcmp (argv[5], "0") != 0);
-                data->attrs = g_ptr_array_new ();
-
-                /*g_warning ("got time_collected=%lld temperature=%g time_powered_on=%lld lstr='%s' is_failing=%d",
-                           data->time_collected,
-                           data->temperature,
-                           data->time_powered_on,
-                           data->last_self_test_result,
-                           data->is_failing);*/
-        }
-
-        id = atoi (argv[6]);
-        name = argv[7];
-        flags = atoi (argv[8]);
-        value = atoi (argv[9]);
-        worst = atoi (argv[10]);
-        threshold = atoi (argv[11]);
-        raw = argv[12];
-
-        /*g_warning ("got id=%d name='%s' flags=0x%04x value=%d worst=%d threshold=%d raw='%s'",
-          id, name, flags, value, worst, threshold, raw);*/
-
-        GValue elem = {0};
-        g_value_init (&elem, SMART_DATA_STRUCT_TYPE);
-        g_value_take_boxed (&elem, dbus_g_type_specialized_construct (SMART_DATA_STRUCT_TYPE));
-        dbus_g_type_struct_set (&elem,
-                                0, id,
-                                1, name,
-                                2, flags,
-                                3, value,
-                                4, worst,
-                                5, threshold,
-                                6, raw,
-                                G_MAXUINT);
-        g_ptr_array_add (data->attrs, g_value_get_boxed (&elem));
-
-
-        /*
-        int n;
-        for (n = 0; n < argc; n++) {
-                printf ("%s = %s\n", col_name[n], argv[n] ? argv[n] : "NULL");
-        }
-        printf("\n");
-        */
-
-out:
-        return 0;
-}
-
-gboolean
-devkit_disks_device_drive_smart_get_historical_data (DevkitDisksDevice     *device,
-                                                     guint64                from,
-                                                     guint64                to,
-                                                     DBusGMethodInvocation *context)
-{
-        char *s;
-        char *disk_id;
-        GTimeVal now;
-        int ret;
-        char *err_msg;
-        DevkitDisksLogger *logger;
-        HistoricalData *data;
-        PolKitCaller *pk_caller;
-
-        disk_id = NULL;
-        pk_caller = NULL;
-
-        if (context != NULL) {
-                if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon,
-                                                                                  context)) == NULL)
-                        goto out;
-        }
-
-        if (context != NULL) {
-                if (!devkit_disks_damon_local_check_auth (
-                            device->priv->daemon,
-                            pk_caller,
-                            "org.freedesktop.devicekit.disks.drive-smart-retrieve-historical-data",
-                            context)) {
-                        goto out;
-                }
-        }
-
-        logger = devkit_disks_daemon_local_get_logger (device->priv->daemon);
-
-        disk_id = drive_get_safe_uuid (device);
-        if (disk_id == NULL) {
-                g_warning ("no drive uuid for %s", device->priv->native_path);
-                throw_error (context, DEVKIT_DISKS_ERROR_FAILED, "No unique disk id for device");
-                goto out;
-        }
-
-        if (from > to) {
-                throw_error (context, DEVKIT_DISKS_ERROR_FAILED, "Malformed time range (from > to)");
-                goto out;
-        }
-
-        if (to == 0) {
-                g_get_current_time (&now);
-                to = (guint64) now.tv_sec;
-        }
-
-        data = g_new0 (HistoricalData, 1);
-        data->array = g_ptr_array_new ();
-        data->cur_rowid = -1;
-
-        s = sqlite3_mprintf ("SELECT"
-                             " SmartEntry.smart_entry_id,"
-                             " SmartEntry.time_collected,"
-                             " SmartEntry.temperature,"
-                             " SmartEntry.time_powered_on,"
-                             " SmartEntry.last_self_test_result,"
-                             " SmartEntry.is_failing,"
-                             " SmartAttr.id,"
-                             " SmartAttr.name,"
-                             " Smartattr.flags, "
-                             " SmartAttr.value,"
-                             " SmartAttr.worst,"
-                             " SmartAttr.threshold,"
-                             " SmartAttr.raw "
-                             "FROM SmartEntry, SmartAttr "
-                             "WHERE"
-                             " SmartEntry.disk_id='%q' AND"
-                             " SmartEntry.smart_entry_id=SmartAttr.smart_entry_id AND"
-                             " SmartEntry.time_collected >= %" G_GUINT64_FORMAT " AND"
-                             " SmartEntry.time_collected <= %" G_GUINT64_FORMAT " "
-                             "ORDER BY SmartEntry.smart_entry_id, SmartAttr.id;",
-                             disk_id, from, to);
-        ret = sqlite3_exec (logger->priv->db,
-                            s,
-                            historical_data_cb,
-                            data,
-                            &err_msg);
-        if (ret != SQLITE_OK) {
-                g_warning ("SQL error: %s", err_msg);
-                sqlite3_free (err_msg);
-        }
-        sqlite3_free (s);
-
-        historical_data_drain (data);
-        dbus_g_method_return (context, data->array);
-        g_ptr_array_foreach (data->array, (GFunc) g_value_array_free, NULL);
-        g_ptr_array_free (data->array, TRUE);
-        g_free (data);
-
-out:
-        g_free (disk_id);
-        if (pk_caller != NULL)
-                polkit_caller_unref (pk_caller);
-        return TRUE;
-}
diff --git a/src/devkit-disks-logger.h b/src/devkit-disks-logger.h
deleted file mode 100644 (file)
index 2ff17ca..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifndef __DEVKIT_DISKS_LOGGER_H__
-#define __DEVKIT_DISKS_LOGGER_H__
-
-#include "devkit-disks-types.h"
-
-G_BEGIN_DECLS
-
-#define DEVKIT_DISKS_TYPE_LOGGER         (devkit_disks_logger_get_type ())
-#define DEVKIT_DISKS_LOGGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DEVKIT_DISKS_TYPE_LOGGER, DevkitDisksLogger))
-#define DEVKIT_DISKS_LOGGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), DEVKIT_DISKS_TYPE_LOGGER, DevkitDisksLoggerClass))
-#define DEVKIT_DISKS_IS_LOGGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DEVKIT_DISKS_TYPE_LOGGER))
-#define DEVKIT_DISKS_IS_LOGGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DEVKIT_DISKS_TYPE_LOGGER))
-#define DEVKIT_DISKS_LOGGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEVKIT_DISKS_TYPE_LOGGER, DevkitDisksLoggerClass))
-
-typedef struct DevkitDisksLoggerClass   DevkitDisksLoggerClass;
-typedef struct DevkitDisksLoggerPrivate DevkitDisksLoggerPrivate;
-
-struct DevkitDisksLogger
-{
-        GObject                   parent;
-        DevkitDisksLoggerPrivate *priv;
-};
-
-struct DevkitDisksLoggerClass
-{
-        GObjectClass   parent_class;
-};
-
-GType              devkit_disks_logger_get_type              (void) G_GNUC_CONST;
-DevkitDisksLogger *devkit_disks_logger_new                   (void);
-void               devkit_disks_logger_record_smart_values   (DevkitDisksLogger *logger,
-                                                              DevkitDisksDevice *device);
-
-G_END_DECLS
-
-#endif /* __DEVKIT_DISKS_LOGGER_H__ */
index 5c0c3f2..1f29ea9 100644 (file)
@@ -27,7 +27,6 @@ G_BEGIN_DECLS
 
 typedef struct DevkitDisksDaemon       DevkitDisksDaemon;
 typedef struct DevkitDisksDevice       DevkitDisksDevice;
-typedef struct DevkitDisksLogger       DevkitDisksLogger;
 typedef struct DevkitDisksMount        DevkitDisksMount;
 typedef struct DevkitDisksMountMonitor DevkitDisksMountMonitor;
 typedef struct DevkitDisksInhibitor    DevkitDisksInhibitor;
diff --git a/src/job-ata-smart-collect.c b/src/job-ata-smart-collect.c
new file mode 100644 (file)
index 0000000..6a61e55
--- /dev/null
@@ -0,0 +1,232 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <glib.h>
+#include <atasmart.h>
+
+static void
+usage (void)
+{
+        fprintf (stderr, "incorrect usage\n");
+}
+
+static guint64 temperature_mkelvin;
+static gboolean has_bad_sectors;
+static gboolean has_bad_attributes;
+static guint64 power_on_seconds;
+
+
+static void
+collect_attrs (SkDisk *d, const SkSmartAttributeParsedData *a, void *user_data)
+{
+        GList **list = user_data;
+        GString *s;
+
+        s = g_string_new (NULL);
+
+        if (strcmp (a->name, "temperature-centi-celsius") == 0 ||
+            strcmp (a->name, "temperature-celsius") == 0 ||
+            strcmp (a->name, "temperature-celsius-2") == 0 ||
+            strcmp (a->name, "airflow-temperature-celsius") == 0) {
+                temperature_mkelvin = a->pretty_value;
+        }
+
+        if (strcmp (a->name, "power-on-minutes") == 0 ||
+            strcmp (a->name, "power-on-seconds") == 0 ||
+            strcmp (a->name, "power-on-half-minutes") == 0 ||
+            strcmp (a->name, "power-on-hours") == 0) {
+                power_on_seconds = a->pretty_value / 1000;
+        }
+
+        if (strcmp (a->name, "reallocated-sector-count") ==0 ||
+            strcmp (a->name, "current-pending-sector") == 0 ||
+            strcmp (a->name, "reallocated-event-count") == 0) {
+                if (a->pretty_value > 0)
+                        has_bad_sectors = TRUE;
+        }
+
+        if (!a->good)
+                has_bad_attributes = TRUE;
+
+        g_string_append_printf (s,
+                                "%d "                             /* id */
+                                "%s "                             /* name */
+                                "%d "                             /* flags */
+                                "%d %d "                          /* online, prefailure */
+                                "%d %d "                          /* current_value, current_value_valid */
+                                "%d %d "                          /* worst_value, worst_value_valid */
+                                "%d %d "                          /* threshold, threshold_valid */
+                                "%d %d "                          /* good, good_valid */
+                                "%d %" G_GUINT64_FORMAT " "       /* pretty_unit, pretty_value */
+                                "%02x %02x %02x %02x %02x %02x",  /* raw[6] */
+
+                                a->id,
+                                a->name,
+                                a->flags,
+                                a->online, a->prefailure,
+
+                                a->current_value, a->current_value_valid,
+                                a->worst_value, a->worst_value_valid,
+                                a->threshold, a->threshold_valid,
+                                a->good, a->good_valid,
+
+                                a->pretty_unit, a->pretty_value,
+
+                                a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5]);
+
+        *list = g_list_prepend (*list, g_string_free (s, FALSE));
+}
+
+int
+main (int argc, char *argv[])
+{
+        int ret;
+        const char *device;
+        SkDisk *d;
+        SkBool smart_is_available;
+        SkBool awake;
+        SkBool good;
+        const SkSmartParsedData *data;
+        GString *s;
+        GList *attrs;
+        GList *l;
+        gboolean nowakeup;
+
+        d = NULL;
+        attrs = NULL;
+        ret = 1;
+
+        s = g_string_new (NULL);
+
+        if (argc != 3) {
+                usage ();
+                goto out;
+        }
+
+        device = argv[1];
+
+        nowakeup = atoi (argv[2]);
+
+        if (sk_disk_open (device, &d) != 0) {
+                fprintf (stderr, "Failed to open disk %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+
+        if (sk_disk_check_sleep_mode (d, &awake) != 0) {
+                fprintf (stderr, "Failed to check if disk %s is awake: %s\n", device, strerror (errno));
+                goto out;
+        }
+
+        /* don't wake up disk unless specically asked to */
+        if (nowakeup && !awake) {
+                fprintf (stderr, "Disk %s is asleep and nowakeup option was passed\n", device);
+                ret = 2;
+                goto out;
+        }
+
+        if (sk_disk_smart_is_available (d, &smart_is_available) != 0) {
+                fprintf (stderr, "Failed to determine if smart is available for %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+
+        /* time collected */
+        g_string_append_printf (s, "%" G_GUINT64_FORMAT "|", (guint64) time (NULL));
+
+        /* version of data */
+        g_string_append_printf (s, "atasmartv0");
+
+        /* main smart data */
+        if (sk_disk_smart_read_data (d) != 0) {
+                fprintf (stderr, "Failed to read smart data for %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+        if (sk_disk_smart_parse (d, &data) != 0) {
+                fprintf (stderr, "Failed to parse smart data for %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+
+        temperature_mkelvin = 0;
+        has_bad_sectors = FALSE;
+        has_bad_attributes = FALSE;
+        power_on_seconds = 0;
+
+        if (sk_disk_smart_parse_attributes (d, collect_attrs, &attrs) != 0) {
+                fprintf (stderr, "Failed to parse smart attributes for %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+        attrs = g_list_reverse (attrs);
+
+        /* health status
+         *
+         * note that this is allowed to fail; some USB disks don't report status
+         */
+        if (sk_disk_smart_status (d, &good) != 0) {
+                fprintf (stderr, "Failed to read smart status for %s: %s\n", device, strerror (errno));
+                g_string_append (s, "|1 0");
+        } else {
+                g_string_append_printf (s, "|%d 1", good ? 0 : 1);
+        }
+        g_string_append_printf (s, " %d %d %lg %" G_GUINT64_FORMAT,
+                                has_bad_sectors,
+                                has_bad_attributes,
+                                temperature_mkelvin / 1000.0,
+                                power_on_seconds);
+
+        g_string_append_printf (s,
+                                "|%d %d %d %d "
+                                "%d %d %d %d "
+                                "%d %d %d",
+                                data->offline_data_collection_status,
+                                data->total_offline_data_collection_seconds,
+                                data->self_test_execution_status,
+                                data->self_test_execution_percent_remaining,
+                                data->short_and_extended_test_available,
+                                data->conveyance_test_available,
+                                data->start_test_available,
+                                data->abort_test_available,
+                                data->short_test_polling_minutes,
+                                data->extended_test_polling_minutes,
+                                data->conveyance_test_polling_minutes);
+
+        /* then each attribute */
+        for (l = attrs; l != NULL; l = l->next) {
+                g_string_append (s, "|");
+                g_string_append (s, l->data);
+        }
+
+        printf ("%s\n", s->str);
+
+        ret = 0;
+
+ out:
+        g_string_free (s, TRUE);
+        g_list_foreach (attrs, (GFunc) g_free, FALSE);
+        g_list_free (attrs);
+
+        if (d != NULL)
+                sk_disk_free (d);
+        return ret;
+}
diff --git a/src/job-ata-smart-selftest.c b/src/job-ata-smart-selftest.c
new file mode 100644 (file)
index 0000000..f7641bb
--- /dev/null
@@ -0,0 +1,137 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <atasmart.h>
+
+static void
+usage (void)
+{
+        g_printerr ("incorrect usage\n");
+}
+
+static const gchar *device;
+static gboolean cancelled = FALSE;
+
+static void
+sigterm_handler (int signum)
+{
+        cancelled = TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+        int ret;
+        SkDisk *d;
+        SkBool smart_is_available;
+        SkSmartSelfTest test;
+        const gchar *taskname;
+
+        d = NULL;
+        ret = 1;
+
+        if (argc != 3) {
+                usage ();
+                goto out;
+        }
+
+        device = argv[1];
+
+        if (strcmp (argv[2], "short") == 0) {
+                test = SK_SMART_SELF_TEST_SHORT;
+                taskname = "ata_smart_selftest_short";
+        } else if (strcmp (argv[2], "extended") == 0) {
+                test = SK_SMART_SELF_TEST_EXTENDED;
+                taskname = "ata_smart_selftest_extended";
+        } else if (strcmp (argv[2], "conveyance") == 0) {
+                test = SK_SMART_SELF_TEST_CONVEYANCE;
+                taskname = "ata_smart_selftest_conveyance";
+        } else {
+                g_printerr ("Unknown test '%s'\n", argv[2]);
+                goto out;
+        }
+
+        if (sk_disk_open (device, &d) != 0) {
+                g_printerr ("Failed to open disk %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+
+        if (sk_disk_smart_is_available (d, &smart_is_available) != 0) {
+                g_printerr ("Failed to determine if smart is available for %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+
+        /* if the user cancels, catch that and abort the test */
+        signal (SIGTERM, sigterm_handler);
+
+        /* progress at 0% initially */
+        g_print ("progress: 0 1 0 %s\n", taskname);
+
+        /* start the test */
+        if (sk_disk_smart_self_test (d, test) != 0) {
+                g_printerr ("Error initiating test on disk %s: %s\n", device, strerror (errno));
+                goto out;
+        }
+
+        /* poll for completion */
+        while (TRUE && !cancelled) {
+                const SkSmartParsedData *data;
+
+                sleep (2);
+
+                if (sk_disk_smart_read_data (d) != 0) {
+                        g_printerr ("Failed to read smart data for %s: %s\n", device, strerror (errno));
+                        goto out;
+                }
+                if (sk_disk_smart_parse (d, &data) != 0) {
+                        g_printerr ("Failed to parse smart data for %s: %s\n", device, strerror (errno));
+                        goto out;
+                }
+
+                if (data->self_test_execution_status != SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS)
+                        break;
+
+                /* update progress */
+                g_print ("progress: 0 1 %d %s\n", 100 - data->self_test_execution_percent_remaining, taskname);
+        }
+
+        /* abort test if cancelled */
+        if (cancelled) {
+                if (sk_disk_smart_self_test (d, SK_SMART_SELF_TEST_ABORT) != 0) {
+                        g_printerr ("Error cancelling test on disk %s: %s\n", device, strerror (errno));
+                        goto out;
+                }
+        }
+
+        ret = 0;
+
+ out:
+        if (d != NULL)
+                sk_disk_free (d);
+        return ret;
+}
diff --git a/src/job-smart-selftest.c b/src/job-smart-selftest.c
deleted file mode 100644 (file)
index 3c84b87..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
-
-#define _LARGEFILE64_SOURCE
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <signal.h>
-
-#include <glib.h>
-
-#include "job-shared.h"
-
-static const char *device;
-static gboolean cancelled = FALSE;
-
-static void
-sigterm_handler (int signum)
-{
-        cancelled = TRUE;
-}
-
-static gboolean
-abort_test (void)
-{
-        gboolean ret;
-        int exit_status;
-        char *standard_error;
-        char *command_line;
-        GError *error;
-
-        ret = FALSE;
-
-        command_line = g_strdup_printf ("smartctl -X %s", device);
-
-        error = NULL;
-        if (!g_spawn_command_line_sync (command_line,
-                                        NULL,
-                                        &standard_error,
-                                        &exit_status,
-                                        &error)) {
-                g_printerr ("cannot spawn '%s': %s\n", command_line, error->message);
-                g_error_free (error);
-                goto out;
-        }
-        if (WEXITSTATUS (exit_status) != 0) {
-                g_printerr ("helper failed with:\n%s", standard_error);
-                goto out;
-        }
-        g_free (standard_error);
-
-        ret = TRUE;
-
-out:
-        return ret;
-}
-
-int
-main (int argc, char **argv)
-{
-        int ret;
-        int exit_status;
-        GError *error;
-        char *command_line;
-        char *standard_output;
-        char *standard_error;
-        const char *test;
-        gboolean captive;
-        const char *result;
-        const char *taskname;
-
-        ret = 1;
-        command_line = NULL;
-        standard_error = NULL;
-        result = NULL;
-
-        if (argc != 4) {
-                g_printerr ("wrong usage\n");
-                goto out;
-        }
-        device = argv[1];
-        test = argv[2];
-        captive = (strcmp (argv[3], "1") == 0);
-
-        if (strcmp (test, "short") == 0) {
-                taskname = "smartselftest_short";
-        } else if (strcmp (test, "long") == 0) {
-                taskname = "smartselftest_long";
-        } else {
-                g_printerr ("unknown test '%s'", test);
-                goto out;
-        }
-
-        g_print ("device   = '%s'\n", device);
-        g_print ("test     = '%s'\n", test);
-        g_print ("captive  = %d\n", captive);
-
-        command_line = g_strdup_printf ("smartctl -t %s %s %s",
-                                        test,
-                                        captive ? "-C" : "",
-                                        device);
-
-        error = NULL;
-        if (!g_spawn_command_line_sync (command_line,
-                                        NULL,
-                                        &standard_error,
-                                        &exit_status,
-                                        &error)) {
-                g_printerr ("cannot spawn '%s'\n", command_line);
-                goto out;
-        }
-        if (WEXITSTATUS (exit_status) != 0) {
-                g_printerr ("helper failed with:\n%s", standard_error);
-                goto out;
-        }
-        g_free (standard_error);
-        standard_error = NULL;
-
-        signal (SIGTERM, sigterm_handler);
-
-        /* ok, now poll every five secs via 'smartctl -c' until the test is done */
-
-        g_free (command_line);
-        command_line = g_strdup_printf ("smartctl -c %s", device);
-
-
-        /* progress at 0% initially */
-        g_print ("progress: 0 1 0 %s\n", taskname);
-
-        while (TRUE) {
-                int n;
-                char **lines;
-                int percentage_done;
-                int exec_status = -1;
-
-                sleep (5);
-
-                if (cancelled) {
-                        g_printerr ("Aborting test and exiting since we caught SIGTERM\n");
-                        abort_test ();
-                        goto out;
-                }
-
-                error = NULL;
-                if (!g_spawn_command_line_sync (command_line,
-                                                &standard_output,
-                                                &standard_error,
-                                                &exit_status,
-                                                &error)) {
-                        g_printerr ("cannot spawn '%s'\n", command_line);
-                        goto out;
-                }
-                if (WEXITSTATUS (exit_status) != 0) {
-                        g_printerr ("helper failed with:\n%s", standard_error);
-                        goto out;
-                }
-                g_free (standard_error);
-                standard_error = NULL;
-
-                lines = g_strsplit (standard_output, "\n", 0);
-                for (n = 0; lines[n] != NULL; n++) {
-                        const char *line = (const char *) lines[n];
-                        if (g_str_has_prefix (line, "Self-test execution status:")) {
-                                int m;
-
-                                for (m = 0; line[m] != '\0'; m++) {
-                                        if (g_ascii_isdigit (line[m]))
-                                                break;
-                                }
-                                if (line[m] != '\0') {
-                                        char *endp;
-
-                                        exec_status = strtol (line + m, &endp, 10);
-                                        if (*endp == ')') {
-                                                /* good */
-                                        } else {
-                                                exec_status = -1;
-                                        }
-                                }
-                        }
-                }
-                g_strfreev (lines);
-
-                /* didn't manage to parse output */
-                if (exec_status == -1) {
-                        g_printerr ("Unexpected output polling drive for selftest completion\n");
-                        abort_test ();
-                        goto out;
-                }
-
-                /* see ataprint.cpp:ataPrintSelectiveSelfTestLog() in smartmontools */
-
-                if ((exec_status >> 4) == 15) {
-                        percentage_done = 100 - (exec_status & 0x0f) * 10;
-                        g_print ("progress: 0 1 %d %s\n", percentage_done, taskname);
-                } else {
-                        switch ((exec_status)>>4){
-                        case  0:
-                                result = "Completed";
-                                break;
-                        case  1:
-                                result = "Aborted_by_host";
-                                break;
-                        case  2:
-                                result = "Interrupted";
-                                break;
-                        case  3:
-                                result = "Fatal_error";
-                                break;
-                        case  4:
-                                result = "Completed_unknown_failure";
-                                break;
-                        case  5:
-                                result = "Completed_electrical_failure";
-                                break;
-                        case  6:
-                                result = "Completed_servo/seek_failure";
-                                break;
-                        case  7:
-                                result = "Completed_read_failure";
-                                break;
-                        case  8:
-                                result = "Completed_handling_damage??";
-                                break;
-                        default:
-                                g_printerr ("Unexpected status %d polling drive for selftest completion\n",
-                                            exec_status);
-                                abort_test ();
-                                goto out;
-                        }
-                        goto test_complete;
-                }
-        }
-
-test_complete:
-        /* send the result of the test back to the daemon */
-        /* g_printerr ("job-smart-selftest: %s\n", result); */
-        ret = 0;
-
-out:
-        g_free (standard_error);
-        g_free (command_line);
-        return ret;
-}
index 80e5b74..b0b5402 100644 (file)
   <!ENTITY ERROR_NOT_LINUX_MD "org.freedesktop.DeviceKit.Disks.Error.NotLinuxMd">
   <!ENTITY ERROR_NOT_LINUX_MD_COMPONENT "org.freedesktop.DeviceKit.Disks.Error.NotLinuxMdComponent">
   <!ENTITY ERROR_NOT_DRIVE "org.freedesktop.DeviceKit.Disks.Error.NotDrive">
-  <!ENTITY ERROR_NOT_SMART_CAPABLE "org.freedesktop.DeviceKit.Disks.Error.NotSmartCapable">
   <!ENTITY ERROR_NOT_SUPPORTED "org.freedesktop.DeviceKit.Disks.Error.NotSupported">
   <!ENTITY ERROR_NOT_FOUND "org.freedesktop.DeviceKit.Disks.Error.NotFound">
+  <!ENTITY ERROR_ATA_SMART_NOT_AVAILABLE "org.freedesktop.DeviceKit.Disks.Error.AtaSmartNotAvailable">
+  <!ENTITY ERROR_ATA_SMART_WOULD_WAKEUP "org.freedesktop.DeviceKit.Disks.Error.AtaSmartWouldWakeup">
   <!ENTITY ERROR_NOT_AUTHORIZED "org.freedesktop.PolicyKit.Error.NotAuthorized">
 ]>
 <node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
 
     <!-- ************************************************************ -->
 
-    <method name="DriveSmartRefreshData">
+    <method name="DriveAtaSmartRefreshData">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <arg name="options" direction="in" type="as">
         <doc:doc><doc:summary>
             The option <doc:tt>nowakeup</doc:tt> can be passed to
             avoid spinning up the disk if it's in a low-power mode.
-            The option <doc:tt>simulate=/path/to/smartctl-output</doc:tt> can be
-            passed to read the given file instead of invoking <doc:tt>smartctl(8)</doc:tt> tool.
+            The option <doc:tt>simulate=</doc:tt> can be
+            passed to use the given string (which is in a format private to DeviceKit-disks) instead
+            of reading it from the disk.
             The <doc:tt>simulate=</doc:tt> option can only be used by the super user.
         </doc:summary></doc:doc>
       </arg>
         <doc:description>
           <doc:para>
             Refreshes the
-            <doc:ulink url="http://en.wikipedia.org/wiki/S.M.A.R.T">S.M.A.R.T.</doc:ulink>
+            <doc:ulink url="http://en.wikipedia.org/wiki/S.M.A.R.T">ATA SMART</doc:ulink>
             data for the given drive. Note that this operation is not run as a job.
           </doc:para>
         </doc:description>
           The caller will need one of the following PolicyKit authorizations:
           <doc:list>
             <doc:item>
-              <doc:term>org.freedesktop.devicekit.disks.drive-smart-refresh</doc:term>
-              <doc:definition>Needed to refresh S.M.A.R.T. data</doc:definition>
+              <doc:term>org.freedesktop.devicekit.disks.drive-ata-smart-refresh</doc:term>
+              <doc:definition>Needed to refresh ATA SMART data</doc:definition>
             </doc:item>
           </doc:list>
         </doc:permission>
         <doc:errors>
           <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
-          <doc:error name="&ERROR_BUSY;">component to add is busy</doc:error>
+          <doc:error name="&ERROR_ATA_SMART_NOT_AVAILABLE;">if ATA SMART isn't available on the disk</doc:error>
+          <doc:error name="&ERROR_ATA_SMART_WOULD_WAKEUP;">If the disk is asleep and the nowakeup option was passed</doc:error>
           <doc:error name="&ERROR_FAILED;">if incoming parameters are invalid or an unknown error occured</doc:error>
-          <doc:error name="&ERROR_NOT_SMART_CAPABLE;">device is not S.M.A.R.T. capable</doc:error>
-          <doc:error name="&ERROR_NOT_DRIVE;">device is not a drive</doc:error>
         </doc:errors>
       </doc:doc>
     </method>
 
-    <method name="DriveSmartGetHistoricalData">
+    <!-- ************************************************************ -->
+
+    <method name="DriveAtaSmartGetHistoricalData">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <arg name="since" direction="in" type="t">
         <doc:doc><doc:summary>
-            Don't fetch S.M.A.R.T. data collected earlier than this point
+            Don't fetch data collected earlier than this point
             in time (seconds since the Epoch Jan 1, 1970 0:00 UTC).
         </doc:summary></doc:doc>
       </arg>
       <arg name="until" direction="in" type="t">
         <doc:doc><doc:summary>
-            Don't fetch S.M.A.R.T. data collected later than this point in
+            Don't fetch data collected later than this point in
             time (seconds since the Epoch Jan 1, 1970 0:00 UTC). If 0 is
             passed the current time will be used.
         </doc:summary></doc:doc>
       </arg>
-      <arg name="data" direction="out" type="a(tdtsba(isiiiis))">
+      <arg name="data" direction="out" type="a(tbbbbdta(usubbybybybbbutay))">
         <doc:doc><doc:summary>
-            An array of historical S.M.A.R.T. data. Each element contains
-            the following members:
-            <doc:list>
-              <doc:item>
-                <doc:term>time</doc:term>
-                <doc:definition>
-                  When the data was collected (seconds since the Epoch Jan 1, 1970 0:00 UTC). Similar to the
-                  <doc:ref type="property" to="Device:drive-smart-time-collected">drive-smart-time-collected</doc:ref>
-                  property.
-                </doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>temperature</doc:term>
-                <doc:definition>
-                  Temperature of the drive. Similar to the
-                  <doc:ref type="property" to="Device:drive-smart-temperature">drive-smart-temperature</doc:ref>
-                  property.
-                </doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>time-powered-on</doc:term>
-                <doc:definition>
-                  Number of the seconds the drive has been powered on. Similar to the
-                  <doc:ref type="property" to="Device:drive-smart-time-powered-on">drive-smart-time-powered-on</doc:ref>
-                  property.
-                </doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>last-self-test-result</doc:term>
-                <doc:definition>
-                  The result of the last self-test. Similar to the
-                  <doc:ref type="property" to="Device:drive-smart-last-self-test-result">drive-smart-last-self-test-result</doc:ref>
-                  property.
-                </doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>is-failing</doc:term>
-                <doc:definition>
-                  If the drive is assessed to be failing. Similar to the
-                  <doc:ref type="property" to="Device:drive-smart-is-failing">drive-smart-is-failing</doc:ref>
-                  property.
-                </doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>attributes</doc:term>
-                <doc:definition>
-                  The attributes of the drive. Similar to the
-                  <doc:ref type="property" to="Device:drive-smart-attributes">drive-smart-attributes</doc:ref>
-                  property.
-                </doc:definition>
-              </doc:item>
-            </doc:list>
+            An array of historical data. Each element contains
+            the following members (TODO).
         </doc:summary></doc:doc>
       </arg>
       <doc:doc>
         <doc:description>
           <doc:para>
-            Retrieves historical S.M.A.R.T. data from the drive in the
+            Retrieves historical ATA SMART data from the drive in the
             given time interval. Note that this is data collected and
-            stored by the host as S.M.A.R.T. capable drives doesn't
-            store or return historical data.
+            stored by the host as ATA SMART capable devices cannot
+            store or return historical data by themselves.
           </doc:para>
         </doc:description>
         <doc:permission>
           The caller will need one of the following PolicyKit authorizations:
           <doc:list>
             <doc:item>
-              <doc:term>org.freedesktop.devicekit.disks.drive-smart-retrieve-historical-data</doc:term>
-              <doc:definition>To retrieve historical S.M.A.R.T. data</doc:definition>
+              <doc:term>org.freedesktop.devicekit.disks.drive-ata-smart-retrieve-historical-data</doc:term>
+              <doc:definition>To retrieve historical ATA SMART data</doc:definition>
             </doc:item>
           </doc:list>
         </doc:permission>
         <doc:errors>
           <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
-          <doc:error name="&ERROR_BUSY;">component to add is busy</doc:error>
+          <doc:error name="&ERROR_ATA_SMART_NOT_AVAILABLE;">if ATA SMART isn't available on the disk</doc:error>
           <doc:error name="&ERROR_FAILED;">if incoming parameters are invalid or an unknown error occured</doc:error>
           <doc:error name="&ERROR_CANCELLED;">if the job was cancelled</doc:error>
-          <doc:error name="&ERROR_NOT_SMART_CAPABLE;">device is not S.M.A.R.T. capable</doc:error>
-          <doc:error name="&ERROR_NOT_DRIVE;">device is not a drive</doc:error>
         </doc:errors>
       </doc:doc>
     </method>
 
     <!-- ************************************************************ -->
 
-    <method name="DriveSmartInitiateSelftest">
+    <method name="DriveAtaSmartInitiateSelftest">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
 
       <arg name="test" direction="in" type="s">
         <doc:doc><doc:summary>
             The name of the test to run; supported values are 'short'
-            (usually less than ten minutes) and 'long' (usually tens
-            of minutes) and 'conveyance' (usually a few minutes). See
-            the smartctl(1) man page for details.
+            (usually less than ten minutes), 'extended' (usually tens
+            of minutes) and 'conveyance' (usually a few minutes).
         </doc:summary></doc:doc>
       </arg>
-      <arg name="captive" direction="in" type="b">
-        <doc:doc><doc:summary>
-            If TRUE then the drive will block access to the drive for
-            the duration of the test. The method will error out if the
-            drive is busy, e.g. if partitions are mounted.
-        </doc:summary></doc:doc>
+      <arg name="options" direction="in" type="as">
+        <doc:doc><doc:summary>Currently unused.</doc:summary></doc:doc>
       </arg>
 
       <doc:doc>
         <doc:description>
           <doc:para>
-            Runs a S.M.A.R.T. self test on the drive. TODO: need to figure out whether we need a return code.
+            Runs a ATA SMART self test on the drive.
           </doc:para>
         </doc:description>
         <doc:permission>
           The caller will need one of the following PolicyKit authorizations:
           <doc:list>
             <doc:item>
-              <doc:term>org.freedesktop.devicekit.disks.drive-smart-selftest</doc:term>
-              <doc:definition>Needed to run S.M.A.R.T. self tests</doc:definition>
+              <doc:term>org.freedesktop.devicekit.disks.drive-ata-smart-selftest</doc:term>
+              <doc:definition>Needed to run ATA SMART self tests</doc:definition>
             </doc:item>
           </doc:list>
         </doc:permission>
         <doc:errors>
           <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
-          <doc:error name="&ERROR_BUSY;">component to add is busy</doc:error>
           <doc:error name="&ERROR_FAILED;">if incoming parameters are invalid or an unknown error occured</doc:error>
           <doc:error name="&ERROR_CANCELLED;">if the job was cancelled</doc:error>
-          <doc:error name="&ERROR_NOT_SMART_CAPABLE;">device is not S.M.A.R.T. capable</doc:error>
-          <doc:error name="&ERROR_NOT_DRIVE;">device is not a drive</doc:error>
+          <doc:error name="&ERROR_ATA_SMART_NOT_AVAILABLE;">device is not ATA SMART capable</doc:error>
         </doc:errors>
       </doc:doc>
     </method>
       </doc:para></doc:description></doc:doc>
     </property>
 
-    <property name="drive-smart-is-capable" type="b" access="read">
+    <!-- **************************************************************************************************** -->
+
+    <property name="drive-ata-smart-is-available" type="b" access="read">
       <doc:doc><doc:description><doc:para>
-            TRUE only if drive is
-            <doc:ulink url="http://en.wikipedia.org/wiki/S.M.A.R.T">S.M.A.R.T.</doc:ulink>
-            capable.
+            TRUE only if drive is capable of reporting
+            <doc:ulink url="http://en.wikipedia.org/wiki/S.M.A.R.T">ATA SMART</doc:ulink>
+            data.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="drive-smart-is-enabled" type="b" access="read">
+    <property name="drive-ata-smart-is-failing" type="b" access="read">
       <doc:doc><doc:description><doc:para>
-            TRUE only if S.M.A.R.T. is enabled for the drive.
+            Set to TRUE if ATA SMART indicates that the disk is failing, TRUE otherwise.
             This property is only valid if
-            <doc:ref type="property" to="Device:drive-smart-time-collected">drive-smart-time-collected</doc:ref>
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero and the property
+            <doc:ref type="property" to="Device:drive-ata-smart-status-valid">drive-ata-smart-status-valid</doc:ref>
+            is TRUE.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-is-failing-valid" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            Set to TRUE only if the property
+            <doc:ref type="property" to="Device:drive-ata-smart-status">drive-ata-smart-status</doc:ref>
+            is valid.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-has-bad-sectors" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            Set to TRUE if ATA SMART indicates that one or more sectors are bad, TRUE otherwise.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-has-bad-attributes" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            Set to TRUE if ATA SMART indicates that one or more attributes are exceeding their threshold, TRUE otherwise.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-temperature-kelvin" type="d" access="read">
+      <doc:doc><doc:description><doc:para>
+            Temperature of the drive in kelvin or 0 if unknown.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-power-on-seconds" type="t" access="read">
+      <doc:doc><doc:description><doc:para>
+            Seconds the drive has been powered on or 0 if unknown.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
             is greater than zero.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="drive-smart-time-collected" type="t" access="read">
+    <property name="drive-ata-smart-time-collected" type="t" access="read">
       <doc:doc><doc:description><doc:para>
             The point in time (seconds since the Epoch Jan 1, 1970
-            0:00 UTC) when the S.M.A.R.T. data was collected. If data
+            0:00 UTC) when ATA SMART data was collected. If data
             was never collected, this property will assume the value
             0.  This property is only valid if
-            <doc:ref type="property" to="Device:drive-smart-is-capable">drive-smart-is-capable</doc:ref>
+            <doc:ref type="property" to="Device:drive-ata-smart-is-available">drive-ata-smart-is-available</doc:ref>
             is TRUE.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="drive-smart-is-failing" type="b" access="read">
+    <property name="drive-ata-smart-offline-data-collection-status" type="u" access="read">
       <doc:doc><doc:description><doc:para>
-            TRUE only if the drive is assessed to be failing.
+            Offline data collection status (TODO: specify values).
             This property is only valid if
-            <doc:ref type="property" to="Device:drive-smart-time-collected">drive-smart-time-collected</doc:ref>
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
             is greater than zero.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="drive-smart-temperature" type="d" access="read">
+    <property name="drive-ata-smart-offline-data-collection-seconds" type="u" access="read">
       <doc:doc><doc:description><doc:para>
-            Temperature of the drive, in degrees celcius.
+            Number of seconds needed to perform offline data collection status.
             This property is only valid if
-            <doc:ref type="property" to="Device:drive-smart-time-collected">drive-smart-time-collected</doc:ref>
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
             is greater than zero.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="drive-smart-time-powered-on" type="t" access="read">
+    <property name="drive-ata-smart-self-test-execution-status" type="u" access="read">
       <doc:doc><doc:description><doc:para>
-            Number of seconds the drive has been powered on.
+            Current status of self test (TODO: specify values).
             This property is only valid if
-            <doc:ref type="property" to="Device:drive-smart-time-collected">drive-smart-time-collected</doc:ref>
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
             is greater than zero.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="drive-smart-last-self-test-result" type="s" access="read">
+    <property name="drive-ata-smart-self-test-execution-percent-remaining" type="u" access="read">
       <doc:doc><doc:description><doc:para>
-            The result of the last self-test. The following values are known:
-            <doc:list>
-              <doc:item>
-                <doc:term>completed_ok</doc:term>
-                <doc:definition>Completed without error or never run</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>not_completed_aborted</doc:term>
-                <doc:definition>Aborted</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>not_completed_aborted_reset</doc:term>
-                <doc:definition>Aborted by host</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>not_completed_unknown_reason</doc:term>
-                <doc:definition>Aborted for unknown reason</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>completed_failed_unknown_reason</doc:term>
-                <doc:definition>Completed, with failure:</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>completed_failed_electrical</doc:term>
-                <doc:definition>Completed, with failure: electrical subsystem</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>completed_failed_servo</doc:term>
-                <doc:definition>Completed, with failure: servo/seek subsystem</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>completed_failed_read</doc:term>
-                <doc:definition>Completed, with failure: read failure</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>completed_failed_damage</doc:term>
-                <doc:definition>Completed, with failure: handling damage</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>unknown</doc:term>
-                <doc:definition>Unknown test result</doc:definition>
-              </doc:item>
-            </doc:list>
+            The progress of an ongoing self test or 0 if no self test is in progress.
             This property is only valid if
-            <doc:ref type="property" to="Device:drive-smart-time-collected">drive-smart-time-collected</doc:ref>
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
             is greater than zero.
       </doc:para></doc:description></doc:doc>
     </property>
-    <property name="drive-smart-attributes" type="a(isiiiis)" access="read">
+    <property name="drive-ata-smart-short-and-extended-self-test-available" type="b" access="read">
       <doc:doc><doc:description><doc:para>
-            An array of S.M.A.R.T. attributes. Each element contains the following members
-            <doc:list>
-              <doc:item>
-                <doc:term>attribute id</doc:term>
-                <doc:definition>the identifier of the attribute</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>attribute name</doc:term>
-                <doc:definition>a human readable name of the attribute or blank</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>flags</doc:term>
-                <doc:definition>Flags (TODO: link to spec)</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>value</doc:term>
-                <doc:definition>The normalized value of the attribute</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>worst</doc:term>
-                <doc:definition>The worst value seen</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>threshold</doc:term>
-                <doc:definition>The threshold of the attribute</doc:definition>
-              </doc:item>
-              <doc:item>
-                <doc:term>raw</doc:term>
-                <doc:definition>The raw vendor specific value of the attribute</doc:definition>
-              </doc:item>
-            </doc:list>
+            Whether the short and extended self-tests are available.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-conveyance-self-test-available" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            Whether the conveyance self-test is available.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-start-self-test-available" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            Whether the start self-test is available.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-abort-self-test-available" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            Whether the abort self-test is available.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
+    <property name="drive-ata-smart-short-self-test-polling-minutes" type="u" access="read">
+      <doc:doc><doc:description><doc:para>
+            Recommended polling time in minutes for short self-test.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-extended-self-test-polling-minutes" type="u" access="read">
+      <doc:doc><doc:description><doc:para>
+            Recommended polling time in minutes for extended self-test.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-conveyance-self-test-polling-minutes" type="u" access="read">
+      <doc:doc><doc:description><doc:para>
+            Recommended polling time in minutes for conveyance self-test.
+            This property is only valid if
+            <doc:ref type="property" to="Device:drive-ata-smart-time-collected">drive-ata-smart-time-collected</doc:ref>
+            is greater than zero.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="drive-ata-smart-attributes" type="a(usubbybybybbbutay)" access="read">
+      <doc:doc><doc:description><doc:para>
+            An array of ATA SMART attributes. Each element contains the following members (TODO).
             This property is only valid if
             <doc:ref type="property" to="Device:drive-smart-time-collected">drive-smart-time-collected</doc:ref>
             is greater than zero.
       </doc:para></doc:description></doc:doc>
     </property>
 
+    <!-- **************************************************************************************************** -->
+
     <property name="linux-md-component-level" type="s" access="read">
       <doc:doc><doc:description><doc:para>
             The RAID level of the array the component is part of. Known values include
index 7ebc931..c0508bd 100644 (file)
@@ -302,24 +302,36 @@ device_removed_signal_handler (DBusGProxy *proxy, const char *object_path, gpoin
   g_print ("removed:   %s\n", object_path);
 }
 
-#define SMART_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_STRING,   \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_INT,      \
-                                                        G_TYPE_STRING,   \
-                                                        G_TYPE_INVALID))
-
-#define HISTORICAL_SMART_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
-                                                                   G_TYPE_UINT64, \
-                                                                   G_TYPE_DOUBLE, \
-                                                                   G_TYPE_UINT64, \
-                                                                   G_TYPE_STRING, \
-                                                                   G_TYPE_BOOLEAN, \
-                                                                   dbus_g_type_get_collection ("GPtrArray", SMART_DATA_STRUCT_TYPE), \
-                                                                   G_TYPE_INVALID))
+#define ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray", \
+                                                                      G_TYPE_UINT, \
+                                                                      G_TYPE_STRING, \
+                                                                      G_TYPE_UINT, \
+                                                                      G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UCHAR, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UCHAR, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UCHAR, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, \
+                                                                      G_TYPE_UINT, G_TYPE_UINT64, \
+                                                                      dbus_g_type_get_collection ("GArray", G_TYPE_UCHAR), \
+                                                                      G_TYPE_INVALID))
+
+#define ATA_SMART_HISTORICAL_SMART_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
+                                                                             G_TYPE_UINT64, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_BOOLEAN, \
+                                                                             G_TYPE_DOUBLE, \
+                                                                             G_TYPE_UINT64, \
+                                                                             dbus_g_type_get_collection ("GPtrArray", ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE), \
+                                                                             G_TYPE_INVALID))
+
+#define LSOF_DATA_STRUCT_TYPE (dbus_g_type_get_struct ("GValueArray",   \
+                                                       G_TYPE_UINT,     \
+                                                       G_TYPE_UINT,     \
+                                                       G_TYPE_STRING,   \
+                                                       G_TYPE_INVALID))
+
 
 /* --- SUCKY CODE BEGIN --- */
 
@@ -415,14 +427,26 @@ typedef struct
         guint optical_disc_num_audio_tracks;
         guint optical_disc_num_sessions;
 
-        gboolean               drive_smart_is_capable;
-        gboolean               drive_smart_is_enabled;
-        guint64                drive_smart_time_collected;
-        gboolean               drive_smart_is_failing;
-        double                 drive_smart_temperature;
-        guint64                drive_smart_time_powered_on;
-        char                  *drive_smart_last_self_test_result;
-        GValue                 drive_smart_attributes;
+        gboolean drive_ata_smart_is_available;
+        gboolean drive_ata_smart_is_failing;
+        gboolean drive_ata_smart_is_failing_valid;
+        gboolean drive_ata_smart_has_bad_sectors;
+        gboolean drive_ata_smart_has_bad_attributes;
+        gdouble drive_ata_smart_temperature_kelvin;
+        guint64 drive_ata_smart_power_on_seconds;
+        guint64 drive_ata_smart_time_collected;
+        guint drive_ata_smart_offline_data_collection_status;
+        guint drive_ata_smart_offline_data_collection_seconds;
+        guint drive_ata_smart_self_test_execution_status;
+        guint drive_ata_smart_self_test_execution_percent_remaining;
+        gboolean drive_ata_smart_short_and_extended_self_test_available;
+        gboolean drive_ata_smart_conveyance_self_test_available;
+        gboolean drive_ata_smart_start_self_test_available;
+        gboolean drive_ata_smart_abort_self_test_available;
+        guint drive_ata_smart_short_self_test_polling_minutes;
+        guint drive_ata_smart_extended_self_test_polling_minutes;
+        guint drive_ata_smart_conveyance_self_test_polling_minutes;
+        GValue drive_ata_smart_attributes;
 
         char    *linux_md_component_level;
         int      linux_md_component_num_raid_devices;
@@ -617,22 +641,46 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props)
         else if (strcmp (key, "optical-disc-num-sessions") == 0)
                 props->optical_disc_num_sessions = g_value_get_uint (value);
 
-        else if (strcmp (key, "drive-smart-is-capable") == 0)
-                props->drive_smart_is_capable = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-smart-is-enabled") == 0)
-                props->drive_smart_is_enabled = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-smart-time-collected") == 0)
-                props->drive_smart_time_collected = g_value_get_uint64 (value);
-        else if (strcmp (key, "drive-smart-is-failing") == 0)
-                props->drive_smart_is_failing = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-smart-temperature") == 0)
-                props->drive_smart_temperature = g_value_get_double (value);
-        else if (strcmp (key, "drive-smart-time-powered-on") == 0)
-                props->drive_smart_time_powered_on = g_value_get_uint64 (value);
-        else if (strcmp (key, "drive-smart-last-self-test-result") == 0)
-                props->drive_smart_last_self_test_result = g_strdup (g_value_get_string (value));
-        else if (strcmp (key, "drive-smart-attributes") == 0) {
-                g_value_copy (value, &(props->drive_smart_attributes));
+        else if (strcmp (key, "drive-ata-smart-is-available") == 0)
+                props->drive_ata_smart_is_available = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-is-failing") == 0)
+                props->drive_ata_smart_is_failing = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-is-failing-valid") == 0)
+                props->drive_ata_smart_is_failing_valid = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-has-bad-sectors") == 0)
+                props->drive_ata_smart_has_bad_sectors = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-has-bad-attributes") == 0)
+                props->drive_ata_smart_has_bad_attributes = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-temperature-kelvin") == 0)
+                props->drive_ata_smart_temperature_kelvin = g_value_get_double (value);
+        else if (strcmp (key, "drive-ata-smart-power-on-seconds") == 0)
+                props->drive_ata_smart_power_on_seconds = g_value_get_uint64 (value);
+        else if (strcmp (key, "drive-ata-smart-time-collected") == 0)
+                props->drive_ata_smart_time_collected = g_value_get_uint64 (value);
+        else if (strcmp (key, "drive-ata-smart-offline-data-collection-status") == 0)
+                props->drive_ata_smart_offline_data_collection_status = g_value_get_uint (value);
+        else if (strcmp (key, "drive-ata-smart-offline-data-collection-seconds") == 0)
+                props->drive_ata_smart_offline_data_collection_seconds = g_value_get_uint (value);
+        else if (strcmp (key, "drive-ata-smart-self-test-execution-status") == 0)
+                props->drive_ata_smart_self_test_execution_status = g_value_get_uint (value);
+        else if (strcmp (key, "drive-ata-smart-self-test-execution-percent-remaining") == 0)
+                props->drive_ata_smart_self_test_execution_percent_remaining = g_value_get_uint (value);
+        else if (strcmp (key, "drive-ata-smart-short-and-extended-self-test-available") == 0)
+                props->drive_ata_smart_short_and_extended_self_test_available = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-conveyance-self-test-available") == 0)
+                props->drive_ata_smart_conveyance_self_test_available = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-start-self-test-available") == 0)
+                props->drive_ata_smart_start_self_test_available = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-abort-self-test-available") == 0)
+                props->drive_ata_smart_abort_self_test_available = g_value_get_boolean (value);
+        else if (strcmp (key, "drive-ata-smart-short-self-test-polling-minutes") == 0)
+                props->drive_ata_smart_short_self_test_polling_minutes = g_value_get_uint (value);
+        else if (strcmp (key, "drive-ata-smart-extended-self-test-polling-minutes") == 0)
+                props->drive_ata_smart_extended_self_test_polling_minutes = g_value_get_uint (value);
+        else if (strcmp (key, "drive-ata-smart-conveyance-self-test-polling-minutes") == 0)
+                props->drive_ata_smart_conveyance_self_test_polling_minutes = g_value_get_uint (value);
+        else if (strcmp (key, "drive-ata-smart-attributes") == 0) {
+                g_value_copy (value, &(props->drive_ata_smart_attributes));
         }
 
         else if (strcmp (key, "linux-md-component-level") == 0)
@@ -729,8 +777,9 @@ device_properties_free (DeviceProperties *props)
         g_free (props->drive_connection_interface);
         g_strfreev (props->drive_media_compatibility);
         g_free (props->drive_media);
-        g_free (props->drive_smart_last_self_test_result);
-        g_value_unset (&(props->drive_smart_attributes));
+
+        g_value_unset (&(props->drive_ata_smart_attributes));
+
         g_free (props->linux_md_component_level);
         g_free (props->linux_md_component_uuid);
         g_free (props->linux_md_component_home_host);
@@ -761,8 +810,8 @@ device_properties_get (DBusGConnection *bus,
         const char *ifname = "org.freedesktop.DeviceKit.Disks.Device";
 
         props = g_new0 (DeviceProperties, 1);
-        g_value_init (&(props->drive_smart_attributes),
-                      dbus_g_type_get_collection ("GPtrArray", SMART_DATA_STRUCT_TYPE));
+        g_value_init (&(props->drive_ata_smart_attributes),
+                      dbus_g_type_get_collection ("GPtrArray", ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE));
 
        prop_proxy = dbus_g_proxy_new_for_name (bus,
                                                 "org.freedesktop.DeviceKit.Disks",
@@ -819,6 +868,127 @@ do_monitor (void)
         return FALSE;
 }
 
+static const gchar *
+print_available (gboolean available)
+{
+        if (available)
+                return "available";
+        else
+                return "not available";
+}
+
+static const gchar *
+get_ata_smart_offline_status (guint offline_status)
+{
+        const gchar *ret;
+
+        switch (offline_status) {
+        case 0: ret = "never collected"; break;
+        case 1: ret = "successful"; break;
+        case 2: ret = "in progress"; break;
+        case 3: ret = "suspended"; break;
+        case 4: ret = "aborted"; break;
+        case 5: ret = "fatal"; break;
+        default: ret = "unknown"; break;
+        }
+
+        return ret;
+}
+
+static const gchar *
+get_ata_smart_self_test_status (guint self_test_status)
+{
+        const gchar *ret;
+
+        switch (self_test_status) {
+        case 0: ret = "success or never"; break;
+        case 1: ret = "aborted"; break;
+        case 2: ret = "interrupted"; break;
+        case 3: ret = "fatal"; break;
+        case 4: ret = "error (unknown)"; break;
+        case 5: ret = "error (electrical)"; break;
+        case 6: ret = "error (servo)"; break;
+        case 7: ret = "error (read)"; break;
+        case 8: ret = "error (handling)"; break;
+        case 15: ret = "in progress"; break;
+        default: ret = "unknown"; break;
+        }
+
+        return ret;
+}
+
+static gchar *
+get_ata_smart_unit (guint unit, guint64 pretty_value)
+{
+        gchar *ret;
+
+        switch (unit) {
+        default:
+        case 0:
+        case 1:
+                ret = g_strdup_printf ("%" G_GUINT64_FORMAT, pretty_value);
+                break;
+
+        case 2:
+                if (pretty_value > 1000 * 60 * 60 * 24) {
+                        ret = g_strdup_printf ("%.3g days", pretty_value / 1000.0 / 60.0 / 60.0 / 24.0);
+                } else if (pretty_value > 1000 * 60 * 60) {
+                        ret = g_strdup_printf ("%.3g hours", pretty_value / 1000.0 / 60.0 / 60.0);
+                } else if (pretty_value > 1000 * 60) {
+                        ret = g_strdup_printf ("%.3g mins", pretty_value / 1000.0 / 60.0);
+                } else if (pretty_value > 1000) {
+                        ret = g_strdup_printf ("%.3g secs", pretty_value / 1000.0);
+                } else {
+                        ret = g_strdup_printf ("%d msec", (gint) pretty_value);
+                }
+                break;
+
+        case 3:
+                ret = g_strdup_printf ("%" G_GUINT64_FORMAT " sectors", pretty_value);
+                break;
+
+        case 4:
+                ret = g_strdup_printf ("%.3gC / %.3gF",
+                                       pretty_value / 1000.0 - 273.15,
+                                       (pretty_value / 1000.0 - 273.15) * 9.0 / 5.0 + 32.0);
+                break;
+        }
+
+        return ret;
+}
+
+static gboolean
+has_colors (void)
+{
+        static gboolean ret = FALSE;
+        static gboolean checked = FALSE;
+
+        if (checked)
+                return ret;
+
+        if (isatty (STDOUT_FILENO)) {
+                ret = TRUE;
+        }
+
+        checked = TRUE;
+
+        return ret;
+}
+
+static void
+begin_highlight (void)
+{
+        if (has_colors ())
+                g_print ("\x1B[1;31m");
+}
+
+static void
+end_highlight (void)
+{
+        if (has_colors ())
+                g_print ("\x1B[0m");
+}
+
 static void
 do_show_info (const char *object_path)
 {
@@ -967,78 +1137,191 @@ do_show_info (const char *object_path)
                 else
                         g_print ("    if speed:              %" G_GINT64_FORMAT " bits/s\n", props->drive_connection_speed);
 
-                if (!props->drive_smart_is_capable) {
-                        g_print ("    S.M.A.R.T.:            not capable\n");
-                } else if (props->drive_smart_time_collected == 0) {
-                        g_print ("    S.M.A.R.T.:            not collected\n");
+                /* ------------------------------------------------------------------------------------------------- */
+
+                if (!props->drive_ata_smart_is_available) {
+                        g_print ("    ATA SMART:             not available\n");
+                } else if (props->drive_ata_smart_time_collected == 0) {
+                        g_print ("    ATA SMART:             Data not collected\n");
                 } else {
                         struct tm *time_tm;
                         time_t time;
                         char time_buf[256];
+                        GPtrArray *p;
 
-                        time = (time_t) props->drive_smart_time_collected;
+                        time = (time_t) props->drive_ata_smart_time_collected;
                         time_tm = localtime (&time);
                         strftime (time_buf, sizeof time_buf, "%c", time_tm);
 
-                        g_print ("    S.M.A.R.T.:            Information collected at %s\n", time_buf);
-                        if (!props->drive_smart_is_capable) {
-                                g_print ("              not capable\n");
-                        } else if (!props->drive_smart_is_enabled) {
-                                g_print ("              not enabled\n");
+
+                        g_print ("    ATA SMART:             Updated at %s\n", time_buf);
+                        if (props->drive_ata_smart_is_failing_valid) {
+                                if (!props->drive_ata_smart_is_failing)
+                                        g_print ("      assessment:          PASSED\n");
+                                else {
+                                        g_print ("      assessment:          ");
+                                        begin_highlight ();
+                                        g_print ("FAILING");
+                                        end_highlight ();
+                                        g_print ("\n");
+                                }
+
+                        } else {
+                                g_print ("      assessment:          Unknown\n");
+                        }
+
+                        if (props->drive_ata_smart_has_bad_sectors) {
+                                begin_highlight ();
+                                g_print ("      bad sectors:         Yes\n");
+                                end_highlight ();
+                        } else {
+                                g_print ("      bad sectors:         None\n");
+                        }
+
+                        if (props->drive_ata_smart_has_bad_attributes) {
+                                begin_highlight ();
+                                g_print ("      attributes:          One ore more attrinbutes exceeds threshold\n");
+                                end_highlight ();
+                        } else {
+                                g_print ("      attributes:          Within threshold\n");
+                        }
+
+                        if (props->drive_ata_smart_temperature_kelvin < 0.1) {
+                                g_print ("      temperature:         Unknown\n");
+                        } else {
+                                gdouble celcius;
+                                gdouble fahrenheit;
+                                celcius = props->drive_ata_smart_temperature_kelvin - 273.15;
+                                fahrenheit = 9.0 * celcius / 5.0 + 32.0;
+                                g_print ("      temperature:         %.3g\302\260 C / %.3g\302\260 F\n", celcius, fahrenheit);
+                        }
+
+                        if (props->drive_ata_smart_power_on_seconds == 0) {
+                                g_print ("      power on hours:      Unknown\n");
+                                g_print ("      powered on:          Unknown\n");
                         } else {
+                                gchar *power_on_text;
+                                guint val;
 
-                                g_print ("      assessment:          %s\n",
-                                         props->drive_smart_is_failing ? "FAILING" : "Passed");
-                                g_print ("      temperature:         %g\302\260 C / %g\302\260 F\n",
-                                         props->drive_smart_temperature,
-                                         9 * props->drive_smart_temperature / 5 + 32);
-                                g_print ("      powered on:          %" G_GUINT64_FORMAT " hours\n", props->drive_smart_time_powered_on / 3600);
-                                //g_print ("      196  Reallocated_Event_Count      0x0032 100   100           0 443023360\n",
-                                g_print ("      =========================================================================\n");
-                                g_print ("      Id   Description                   Flags Value Worst Threshold       Raw\n");
-                                g_print ("      =========================================================================\n");
-                                GPtrArray *p;
-                                p = g_value_get_boxed (&(props->drive_smart_attributes));
-                                for (m = 0; m < p->len; m++) {
-                                        gint attr_id;
-                                        gchar *attr_desc;
-                                        gint attr_flags;
-                                        gint attr_value;
-                                        gint attr_worst;
-                                        gint attr_threshold;
-                                        gchar *attr_raw;
-                                        GValue elem = {0};
-
-                                        g_value_init (&elem, SMART_DATA_STRUCT_TYPE);
-                                        g_value_set_static_boxed (&elem, p->pdata[m]);
-
-                                        dbus_g_type_struct_get (&elem,
-                                                                0, &(attr_id),
-                                                                1, &(attr_desc),
-                                                                2, &(attr_flags),
-                                                                3, &(attr_value),
-                                                                4, &(attr_worst),
-                                                                5, &(attr_threshold),
-                                                                6, &(attr_raw),
-                                                                G_MAXUINT);
-
-                                        g_print ("      %3d  %-28s 0x%04x %5d %5d %9d %s\n",
-                                                 attr_id,
-                                                 attr_desc,
-                                                 attr_flags,
-                                                 attr_value,
-                                                 attr_worst,
-                                                 attr_threshold,
-                                                 attr_raw);
-
-                                        g_free (attr_desc);
-                                        g_free (attr_raw);
-
-                                        g_value_unset (&elem);
+                                val = props->drive_ata_smart_power_on_seconds;
+
+                                if (val > 60 * 60 * 24) {
+                                        power_on_text = g_strdup_printf (_("%.3g days"), val / 60.0 / 60.0 / 24.0);
+                                } else {
+                                        power_on_text = g_strdup_printf (_("%.3g hours"), val / 60.0 / 60.0);
                                 }
+
+                                g_print ("      powered on:          %s\n", power_on_text);
+                                g_free (power_on_text);
+                        }
+
+
+                        g_print ("      offline data:        %s (%d second(s) to complete)\n", get_ata_smart_offline_status (props->drive_ata_smart_offline_data_collection_status), props->drive_ata_smart_offline_data_collection_seconds);
+                        g_print ("      self-test status:    %s (%d%% remaining)\n", get_ata_smart_self_test_status (props->drive_ata_smart_self_test_execution_status), props->drive_ata_smart_self_test_execution_percent_remaining);
+                        g_print ("      ext./short test:     %s\n", print_available (props->drive_ata_smart_short_and_extended_self_test_available));
+                        g_print ("      conveyance test:     %s\n", print_available (props->drive_ata_smart_conveyance_self_test_available));
+                        g_print ("      start test:          %s\n", print_available (props->drive_ata_smart_start_self_test_available));
+                        g_print ("      abort test:          %s\n", print_available (props->drive_ata_smart_abort_self_test_available));
+                        g_print ("      short test:          %3d minute(s) recommended polling time\n", props->drive_ata_smart_short_self_test_polling_minutes);
+                        g_print ("      ext. test:           %3d minute(s) recommended polling time\n", props->drive_ata_smart_extended_self_test_polling_minutes);
+                        g_print ("      conveyance test:     %3d minute(s) recommended polling time\n", props->drive_ata_smart_conveyance_self_test_polling_minutes);
+                        g_print ("===============================================================================\n");
+                        g_print (" Attribute       Current/Worst/Threshold  Status   Value       Type     Updates\n");
+                        g_print ("===============================================================================\n");
+                        p = g_value_get_boxed (&(props->drive_ata_smart_attributes));
+                        for (m = 0; m < p->len; m++) {
+                                GValue elem = {0};
+                                guint id;
+                                gchar *name;
+                                guint flags;
+                                gboolean online, prefailure;
+                                guchar current;
+                                gboolean current_valid;
+                                guchar worst;
+                                gboolean worst_valid;
+                                guchar threshold;
+                                gboolean threshold_valid;
+                                gboolean good, good_valid;
+                                guint pretty_unit;
+                                guint64 pretty_value;
+                                gchar *pretty;
+                                const gchar *assessment;
+                                const gchar *type;
+                                const gchar *updates;
+                                gboolean do_highlight;
+                                GArray *raw_data;
+
+                                g_value_init (&elem, ATA_SMART_DATA_ATTRIBUTE_STRUCT_TYPE);
+                                g_value_set_static_boxed (&elem, p->pdata[m]);
+
+                                dbus_g_type_struct_get (&elem,
+                                                         0, &id,
+                                                         1, &name,
+                                                         2, &flags,
+                                                         3, &online,
+                                                         4, &prefailure,
+                                                         5, &current,
+                                                         6, &current_valid,
+                                                         7, &worst,
+                                                         8, &worst_valid,
+                                                         9, &threshold,
+                                                        10, &threshold_valid,
+                                                        11, &good,
+                                                        12, &good_valid,
+                                                        13, &pretty_unit,
+                                                        14, &pretty_value,
+                                                        15, &raw_data,
+                                                        G_MAXUINT);
+
+                                pretty = get_ata_smart_unit (pretty_unit, pretty_value);
+
+                                do_highlight = FALSE;
+                                if (!good_valid)
+                                        assessment = " n/a";
+                                else if (good)
+                                        assessment = "good";
+                                else {
+                                        assessment = "FAIL";
+                                        do_highlight = TRUE;
+                                }
+
+                                if (online)
+                                        updates = "Online ";
+                                else
+                                        updates = "Offline";
+
+                                if (prefailure)
+                                        type = "Prefail";
+                                else
+                                        type = "Old-age";
+
+                                if (do_highlight)
+                                        begin_highlight ();
+
+                                g_print (" %-27s %3d/%3d/%3d   %s    %-11s %s  %s\n",
+                                         name,
+                                         current,
+                                         worst,
+                                         threshold,
+                                         assessment,
+                                         pretty,
+                                         type,
+                                         updates);
+
+                                if (do_highlight)
+                                        end_highlight ();
+
+
+                                g_free (pretty);
+
+                                g_array_free (raw_data, TRUE);
+                                g_value_unset (&elem);
                         }
                 }
 
+                /* ------------------------------------------------------------------------------------------------- */
+
+
         }
         device_properties_free (props);
 }
@@ -1292,10 +1575,10 @@ main (int argc, char **argv)
                 { NULL }
         };
 
-        ret = 1;
-
         setlocale (LC_ALL, "");
 
+        ret = 1;
+
         g_type_init ();
 
         context = g_option_context_new ("DeviceKit-disks tool");