rework some of the ATA SMART stuff, go back to using sqlite
authorDavid Zeuthen <davidz@redhat.com>
Mon, 6 Apr 2009 15:51:15 +0000 (11:51 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Mon, 6 Apr 2009 15:51:15 +0000 (11:51 -0400)
configure.ac
src/Makefile.am
src/devkit-disks-ata-smart-db.c [new file with mode: 0644]
src/devkit-disks-ata-smart-db.h [new file with mode: 0644]
src/devkit-disks-daemon.c
src/devkit-disks-daemon.h
src/devkit-disks-device.c
src/devkit-disks-device.h
src/devkit-disks-types.h
src/job-ata-smart-collect.c
src/org.freedesktop.DeviceKit.Disks.Device.xml

index 14d178f..5d3dd71 100644 (file)
@@ -107,6 +107,20 @@ if test "x$GCC" = "xyes"; then
   changequote([,])dnl
 fi
 
+have_zlib="false"
+AC_CHECK_LIB([z], [compress2], [have_zlib="true"])
+if test x$have_zlib != "xtrue"; then
+  AC_MSG_ERROR([zlib is needed])
+fi
+ZLIB_CFLAGS=
+ZLIB_LIBS="-lz"
+AC_SUBST(ZLIB_CFLAGS)
+AC_SUBST(ZLIB_LIBS)
+
+PKG_CHECK_MODULES(SQLITE3, [sqlite3])
+AC_SUBST(SQLITE3_CFLAGS)
+AC_SUBST(SQLITE3_LIBS)
+
 PKG_CHECK_MODULES(DEVKIT, [devkit-gobject >= 002])
 AC_SUBST(DEVKIT_GOBJECT_CFLAGS)
 AC_SUBST(DEVKIT_GOBJECT_LIBS)
@@ -139,7 +153,7 @@ PKG_CHECK_MODULES(DEVMAPPER, [devmapper >= 1.02])
 AC_SUBST(DEVMAPPER_CFLAGS)
 AC_SUBST(DEVMAPPER_LIBS)
 
-PKG_CHECK_MODULES(LIBATASMART, [libatasmart >= 0.2])
+PKG_CHECK_MODULES(LIBATASMART, [libatasmart >= 0.5])
 AC_SUBST(LIBATASMART_CFLAGS)
 AC_SUBST(LIBATASMART_LIBS)
 
index a59cb0b..e2e40ed 100644 (file)
@@ -50,6 +50,7 @@ devkit_disks_daemon_SOURCES =                                                 \
        devkit-disks-mount.h            devkit-disks-mount.c            \
        devkit-disks-mount-monitor.h    devkit-disks-mount-monitor.c    \
        devkit-disks-inhibitor.h        devkit-disks-inhibitor.c        \
+       devkit-disks-ata-smart-db.h     devkit-disks-ata-smart-db.c     \
        devkit-disks-poller.h           devkit-disks-poller.c           \
        main.c                                                          \
        $(BUILT_SOURCES)
@@ -57,16 +58,25 @@ devkit_disks_daemon_SOURCES =                                               \
 devkit_disks_daemon_CPPFLAGS =                                 \
        -I$(top_srcdir)/src                             \
        -DG_LOG_DOMAIN=\"devkit-disks-daemon\"          \
-       $(SQLITE3_CFLAGS)                               \
        $(DISABLE_DEPRECATED)                           \
-       $(AM_CPPFLAGS)
+       $(AM_CPPFLAGS)                                  \
+       $(NULL)
+
+devkit_disks_daemon_CFLAGS =                           \
+       $(ZLIB_CFLAGS)                                  \
+       $(SQLITE3_CFLAGS)                               \
+       $(LIBATASMART_CFLAGS)                           \
+       $(NULL)
 
 devkit_disks_daemon_LDADD =                            \
        $(GIO_LIBS)                                     \
-       $(SQLITE3_LIBS)                                 \
        $(DBUS_GLIB_LIBS)                               \
        $(POLKIT_DBUS_LIBS)                             \
-       $(DEVKIT_LIBS)
+       $(DEVKIT_LIBS)                                  \
+       $(ZLIB_LIBS)                                    \
+       $(SQLITE3_LIBS)                                 \
+       $(LIBATASMART_LIBS)                             \
+       $(NULL)
 
 noinst_LTLIBRARIES = libpartutil.la
 libpartutil_la_SOURCES = partutil.h partutil.c
@@ -178,4 +188,3 @@ clean-local :
 install-data-local:
        -$(mkdir_p) $(DESTDIR)$(localstatedir)/lib/DeviceKit-disks
        -chmod 0700 $(DESTDIR)$(localstatedir)/lib/DeviceKit-disks
-       -$(mkdir_p) $(DESTDIR)$(localstatedir)/lib/DeviceKit-disks/ata-smart
diff --git a/src/devkit-disks-ata-smart-db.c b/src/devkit-disks-ata-smart-db.c
new file mode 100644 (file)
index 0000000..e3410b9
--- /dev/null
@@ -0,0 +1,784 @@
+/* -*- 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 <zlib.h>
+
+#include "devkit-disks-daemon.h"
+#include "devkit-disks-device.h"
+#include "devkit-disks-device-private.h"
+#include "devkit-disks-ata-smart-db.h"
+
+struct DevkitDisksAtaSmartDbPrivate
+{
+        sqlite3 *db;
+};
+
+G_DEFINE_TYPE (DevkitDisksAtaSmartDb, devkit_disks_ata_smart_db, G_TYPE_OBJECT)
+
+#define DEVKIT_DISKS_ATA_SMART_DB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEVKIT_DISKS_TYPE_DEVICE, DevkitDisksDevicePrivate))
+
+
+static void
+devkit_disks_ata_smart_db_finalize (GObject *object)
+{
+        DevkitDisksAtaSmartDb *db = DEVKIT_DISKS_ATA_SMART_DB (object);
+
+        if (db->priv->db != NULL)
+                sqlite3_close (db->priv->db);
+
+        if (G_OBJECT_CLASS (devkit_disks_ata_smart_db_parent_class)->finalize != NULL)
+                G_OBJECT_CLASS (devkit_disks_ata_smart_db_parent_class)->finalize (object);
+}
+
+static void
+devkit_disks_ata_smart_db_class_init (DevkitDisksAtaSmartDbClass *klass)
+{
+        GObjectClass *object_class = (GObjectClass *) klass;
+
+        g_type_class_add_private (klass, sizeof (DevkitDisksAtaSmartDbPrivate));
+
+        object_class->finalize = devkit_disks_ata_smart_db_finalize;
+}
+
+static void
+devkit_disks_ata_smart_db_init (DevkitDisksAtaSmartDb *db)
+{
+        gint ret;
+        gchar *err_msg;
+
+        db->priv = G_TYPE_INSTANCE_GET_PRIVATE (db, DEVKIT_DISKS_TYPE_ATA_SMART_DB, DevkitDisksAtaSmartDbPrivate);
+
+        ret = sqlite3_open_v2 (PACKAGE_LOCALSTATE_DIR "/lib/DeviceKit-disks/ata-smart-db.sqlite3",
+                               &db->priv->db,
+                               SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+                               NULL);
+        if (ret != SQLITE_OK) {
+                g_warning ("error opening sqlite3 database at "
+                           PACKAGE_LOCALSTATE_DIR "/lib/DeviceKit-disks/ata-smart-db.sqlite3"
+                           ": %s", sqlite3_errmsg (db->priv->db));
+                sqlite3_close (db->priv->db);
+                g_object_unref (db);
+                goto out;
+        }
+
+        /* create tables */
+        ret = sqlite3_exec (db->priv->db,
+                            "CREATE TABLE AtaSmartEntry ("
+                            "  disk_id TEXT, "
+                            "  time_collected INTEGER, "
+                            "  is_failing INTEGER,"
+                            "  is_failing_valid INTEGER,"
+                            "  has_bad_sectors INTEGER,"
+                            "  has_bad_attributes INTEGER,"
+                            "  temperature_kelvin REAL,"
+                            "  power_on_secs INTEGER,"
+                            "  compressed_data BLOB "
+                            ");",
+                            NULL,
+                            NULL,
+                            &err_msg);
+        if (ret != SQLITE_OK) {
+                g_warning ("SQL error creating tables: %s", err_msg);
+                sqlite3_free (err_msg);
+        }
+
+ out:
+        ;
+}
+
+DevkitDisksAtaSmartDb *
+devkit_disks_ata_smart_db_new (void)
+{
+        return DEVKIT_DISKS_ATA_SMART_DB (g_object_new (DEVKIT_DISKS_TYPE_ATA_SMART_DB, NULL));
+}
+
+static gchar *
+get_disk_id (DevkitDisksDevice *device)
+{
+        gchar *s;
+        gchar *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_ata_smart_db_add_entry (DevkitDisksAtaSmartDb *db,
+                                     DevkitDisksDevice     *device,
+                                     time_t                 time_collected,
+                                     gboolean               is_failing,
+                                     gboolean               is_failing_valid,
+                                     gboolean               has_bad_sectors,
+                                     gboolean               has_bad_attributes,
+                                     gdouble                temperature_kelvin,
+                                     guint64                power_on_seconds,
+                                     const void            *blob,
+                                     gsize                  blob_size)
+{
+        gint ret;
+        char *s;
+        gchar *disk_id;
+        sqlite3_stmt *stmt;
+        guchar *compressed_blob;
+        uLongf compressed_blob_size;
+
+        s = NULL;
+        stmt = NULL;
+        disk_id = NULL;
+        compressed_blob = NULL;
+
+        if (db->priv->db == NULL) {
+                g_warning ("No database");
+                goto out;
+        }
+
+        disk_id = get_disk_id (device);
+        if (disk_id == NULL) {
+                g_warning ("Error getting stable ID for device");
+                goto out;
+        }
+
+        /* compress the data */
+        compressed_blob = g_new0 (guchar, blob_size * 3 / 2 + 32);
+        ret = compress2 (compressed_blob, &compressed_blob_size,
+                         blob, blob_size,
+                         1);
+        if (ret != Z_OK) {
+                g_warning ("Error compressing blob: %d", ret);
+                goto out;
+        }
+
+        //g_debug ("Compressed %ld bytes into %ld bytes", blob_size, compressed_blob_size);
+
+        /* insert it into the database */
+        s = sqlite3_mprintf ("INSERT INTO AtaSmartEntry "
+                             "(disk_id, "
+                             "time_collected, "
+                             "is_failing, "
+                             "is_failing_valid, "
+                             "has_bad_sectors, "
+                             "has_bad_attributes, "
+                             "temperature_kelvin, "
+                             "power_on_secs, "
+                             "compressed_data) "
+                             "VALUES ('%q', "
+                             "%" G_GUINT64_FORMAT ", "
+                             "%d, "
+                             "%d, "
+                             "%d, "
+                             "%d, "
+                             "%g, "
+                             "%" G_GUINT64_FORMAT ", "
+                             "?)",
+                             disk_id,
+                             (guint64) time_collected,
+                             is_failing,
+                             is_failing_valid,
+                             has_bad_sectors,
+                             has_bad_attributes,
+                             temperature_kelvin,
+                             power_on_seconds);
+        ret = sqlite3_prepare_v2 (db->priv->db,
+                                  s,
+                                  -1,
+                                  &stmt,
+                                  NULL);
+        if (ret != SQLITE_OK) {
+                g_warning ("SQL error preparing statement: %d", ret);
+                goto out;
+        }
+        ret = sqlite3_bind_blob (stmt,
+                                 1,
+                                 compressed_blob,
+                                 compressed_blob_size,
+                                 SQLITE_STATIC);
+        if (ret != SQLITE_OK) {
+                g_warning ("SQL error binding BLOB: %d", ret);
+                goto out;
+        }
+        ret = sqlite3_step (stmt);
+        if (ret != SQLITE_DONE) {
+                g_warning ("SQL error executing statement: %d", ret);
+                goto out;
+        }
+
+ out:
+        g_free (compressed_blob);
+        g_free (disk_id);
+        if (s != NULL)
+                sqlite3_free (s);
+        if (stmt != NULL)
+                sqlite3_finalize (stmt);
+}
+
+void
+devkit_disks_ata_smart_db_delete_entries (DevkitDisksAtaSmartDb *db,
+                                          time_t                 cut_off_point)
+{
+        char *s;
+        gint ret;
+        sqlite3_stmt *stmt;
+
+        s = NULL;
+        stmt = NULL;
+
+        s = sqlite3_mprintf ("DELETE FROM AtaSmartEntry WHERE time_collected < %"G_GUINT64_FORMAT ";",
+                             (guint64) cut_off_point);
+        ret = sqlite3_prepare_v2 (db->priv->db,
+                                  s,
+                                  -1,
+                                  &stmt,
+                                  NULL);
+        if (ret != SQLITE_OK) {
+                g_warning ("SQL error preparing statement: %d", ret);
+                goto out;
+        }
+        ret = sqlite3_step (stmt);
+        if (ret != SQLITE_DONE) {
+                g_warning ("SQL error executing statement: %d", ret);
+                goto out;
+        }
+
+ out:
+        if (s != NULL)
+                sqlite3_free (s);
+        if (stmt != NULL)
+                sqlite3_finalize (stmt);
+}
+
+gboolean
+devkit_disks_ata_smart_db_get_entries (DevkitDisksAtaSmartDb              *db,
+                                       DevkitDisksDevice                  *device,
+                                       time_t                              since,
+                                       time_t                              until,
+                                       guint64                             spacing,
+                                       DevkitDisksAtaSmartDbGetEntriesFunc callback,
+                                       gpointer                            user_data)
+{
+        gboolean ret;
+        char *s;
+        sqlite3_stmt *stmt;
+        gchar *disk_id;
+        guint64 last_time_collected;
+
+        ret = FALSE;
+        stmt = NULL;
+
+        if (db->priv->db == NULL) {
+                g_warning ("No database");
+                goto out;
+        }
+
+        disk_id = get_disk_id (device);
+        if (disk_id == NULL) {
+                g_warning ("Error getting stable ID for device");
+                goto out;
+        }
+
+        s = sqlite3_mprintf ("SELECT"
+                             " AtaSmartEntry.time_collected,"
+                             " AtaSmartEntry.compressed_data, "
+                             " AtaSmartEntry.is_failing, "
+                             " AtaSmartEntry.is_failing_valid, "
+                             " AtaSmartEntry.has_bad_sectors, "
+                             " AtaSmartEntry.has_bad_attributes, "
+                             " AtaSmartEntry.temperature_kelvin, "
+                             " AtaSmartEntry.power_on_secs "
+                             "FROM AtaSmartEntry "
+                             "WHERE"
+                             " AtaSmartEntry.disk_id='%q' AND"
+                             " AtaSmartEntry.time_collected >= %" G_GUINT64_FORMAT " AND"
+                             " AtaSmartEntry.time_collected <= %" G_GUINT64_FORMAT " "
+                             "ORDER BY AtaSmartEntry.time_collected;",
+                             disk_id,
+                             since,
+                             until);
+        ret = sqlite3_prepare_v2 (db->priv->db,
+                                  s,
+                                  -1,
+                                  &stmt,
+                                  NULL);
+        if (ret != SQLITE_OK) {
+                g_warning ("SQL error preparing statement: %d", ret);
+                goto out;
+        }
+
+        last_time_collected = 0;
+        do {
+                guint64 time_collected;
+                const void *compressed_blob;
+                gsize compressed_blob_size;
+                static guchar blob[2048]; /* assume 2k is enough */
+                uLongf blob_size;
+                gint rc;
+                gboolean    is_failing;
+                gboolean    is_failing_valid;
+                gboolean    has_bad_sectors;
+                gboolean    has_bad_attributes;
+                gdouble     temperature_kelvin;
+                guint64     power_on_seconds;
+
+                ret = sqlite3_step (stmt);
+
+                if (ret == SQLITE_DONE)
+                        break;
+
+                if (ret != SQLITE_ROW) {
+                        g_warning ("SQL error stepping: %d", ret);
+                        goto out;
+                }
+
+                time_collected = sqlite3_column_int64 (stmt, 0);
+
+                if (time_collected < (guint64) since)
+                        continue;
+                if (time_collected > (guint64) until)
+                        continue;
+                if (time_collected - last_time_collected < spacing)
+                        continue;
+
+                last_time_collected = time_collected;
+
+
+                compressed_blob = sqlite3_column_blob (stmt, 1);
+                compressed_blob_size = sqlite3_column_bytes (stmt, 1);
+
+                is_failing = sqlite3_column_int (stmt, 2);
+                is_failing_valid = sqlite3_column_int (stmt, 3);
+                has_bad_sectors = sqlite3_column_int (stmt, 4);
+                has_bad_attributes = sqlite3_column_int (stmt, 5);
+                temperature_kelvin = sqlite3_column_double (stmt, 6);
+                power_on_seconds = sqlite3_column_int64 (stmt, 7);
+
+                blob_size = sizeof blob;
+                rc = uncompress (blob, &blob_size, compressed_blob, compressed_blob_size);
+                if (rc != Z_OK) {
+                        g_warning ("Decompression of compressed blob of size %d from time %" G_GUINT64_FORMAT
+                                   " for device %s FAILED with return code %d. Ignoring.",
+                                   (gint) compressed_blob_size,
+                                   time_collected,
+                                   device->priv->device_file,
+                                   rc);
+                        continue;
+                }
+
+                //g_debug ("haz row %ld %ld %ld", time_collected, compressed_blob_size, blob_size);
+
+                callback (time_collected,
+                          is_failing,
+                          is_failing_valid,
+                          has_bad_sectors,
+                          has_bad_attributes,
+                          temperature_kelvin,
+                          power_on_seconds,
+                          blob,
+                          blob_size,
+                          user_data);
+
+        } while (ret != SQLITE_DONE);
+
+
+ out:
+        g_free (disk_id);
+        if (s != NULL)
+                sqlite3_free (s);
+        if (stmt != NULL)
+                sqlite3_finalize (stmt);
+        return ret;
+}
+
+#if 0
+void
+devkit_disks_ata_smart_db_record_smart_values (DevkitDisksAtaSmartDb *ata_smart_db,
+                                         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 (ata_smart_db != NULL);
+        g_return_if_fail (ata_smart_db->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 (ata_smart_db->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 (ata_smart_db->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 (ata_smart_db->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;
+        DevkitDisksAtaSmartDb *ata_smart_db;
+        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;
+                }
+        }
+
+        ata_smart_db = devkit_disks_daemon_local_get_ata_smart_db (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 (ata_smart_db->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;
+}
+#endif
diff --git a/src/devkit-disks-ata-smart-db.h b/src/devkit-disks-ata-smart-db.h
new file mode 100644 (file)
index 0000000..100d0ee
--- /dev/null
@@ -0,0 +1,85 @@
+/* -*- 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_ATA_SMART_DB_H__
+#define __DEVKIT_DISKS_ATA_SMART_DB_H__
+
+#include "devkit-disks-types.h"
+
+G_BEGIN_DECLS
+
+#define DEVKIT_DISKS_TYPE_ATA_SMART_DB         (devkit_disks_ata_smart_db_get_type ())
+#define DEVKIT_DISKS_ATA_SMART_DB(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DEVKIT_DISKS_TYPE_ATA_SMART_DB, DevkitDisksAtaSmartDb))
+#define DEVKIT_DISKS_ATA_SMART_DB_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), DEVKIT_DISKS_TYPE_ATA_SMART_DB, DevkitDisksAtaSmartDbClass))
+#define DEVKIT_DISKS_IS_ATA_SMART_DB(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DEVKIT_DISKS_TYPE_ATA_SMART_DB))
+#define DEVKIT_DISKS_IS_ATA_SMART_DB_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DEVKIT_DISKS_TYPE_ATA_SMART_DB))
+#define DEVKIT_DISKS_ATA_SMART_DB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEVKIT_DISKS_TYPE_ATA_SMART_DB, DevkitDisksAtaSmartDbClass))
+
+typedef struct DevkitDisksAtaSmartDbClass   DevkitDisksAtaSmartDbClass;
+typedef struct DevkitDisksAtaSmartDbPrivate DevkitDisksAtaSmartDbPrivate;
+
+struct DevkitDisksAtaSmartDb
+{
+        GObject                       parent;
+        DevkitDisksAtaSmartDbPrivate *priv;
+};
+
+struct DevkitDisksAtaSmartDbClass
+{
+        GObjectClass parent_class;
+};
+
+typedef gboolean (*DevkitDisksAtaSmartDbGetEntriesFunc) (time_t      time_collected,
+                                                         gboolean    is_failing,
+                                                         gboolean    is_failing_valid,
+                                                         gboolean    has_bad_sectors,
+                                                         gboolean    has_bad_attributes,
+                                                         gdouble     temperature_kelvin,
+                                                         guint64     power_on_seconds,
+                                                         const void *blob,
+                                                         gsize       blob_size,
+                                                         gpointer    user_data);
+
+GType                  devkit_disks_ata_smart_db_get_type       (void) G_GNUC_CONST;
+DevkitDisksAtaSmartDb *devkit_disks_ata_smart_db_new            (void);
+void                   devkit_disks_ata_smart_db_add_entry      (DevkitDisksAtaSmartDb              *db,
+                                                                 DevkitDisksDevice                  *device,
+                                                                 time_t                              time_collected,
+                                                                 gboolean                            is_failing,
+                                                                 gboolean                            is_failing_valid,
+                                                                 gboolean                            has_bad_sectors,
+                                                                 gboolean                            has_bad_attributes,
+                                                                 gdouble                             temperature_kelvin,
+                                                                 guint64                             power_on_seconds,
+                                                                 const void                         *blob,
+                                                                 gsize                               blob_size);
+void                   devkit_disks_ata_smart_db_delete_entries (DevkitDisksAtaSmartDb              *db,
+                                                                 time_t                              cut_off_point);
+gboolean               devkit_disks_ata_smart_db_get_entries    (DevkitDisksAtaSmartDb              *db,
+                                                                 DevkitDisksDevice                  *device,
+                                                                 time_t                              since,
+                                                                 time_t                              until,
+                                                                 guint64                             spacing,
+                                                                 DevkitDisksAtaSmartDbGetEntriesFunc callback,
+                                                                 gpointer                            user_data);
+
+G_END_DECLS
+
+#endif /* __DEVKIT_DISKS_ATA_SMART_DB_H__ */
index 8cad73c..3eaf065 100644 (file)
 
 #define _GNU_SOURCE 1
 
+/* ---------------------------------------------------------------------------------------------------- */
+/* We might want these things to be configurable; for now they are hardcoded */
+
+/* update ATA SMART every 30 minutes */
+#define ATA_SMART_REFRESH_INTERVAL_SECONDS (30*60)
+
+/* clean up old ATA SMART entries every 24 hours (and on startup) */
+#define ATA_SMART_CLEANUP_INTERVAL_SECONDS (24*60*60)
+
+/* delete entries older than five days */
+#define ATA_SMART_KEEP_ENTRIES_SECONDS (5*24*60*60)
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -57,6 +71,7 @@
 #include "devkit-disks-mount-monitor.h"
 #include "devkit-disks-poller.h"
 #include "devkit-disks-inhibitor.h"
+#include "devkit-disks-ata-smart-db.h"
 
 #include "devkit-disks-daemon-glue.h"
 #include "devkit-disks-marshal.h"
@@ -102,7 +117,10 @@ struct DevkitDisksDaemonPrivate
 
         DevkitDisksMountMonitor *mount_monitor;
 
+        DevkitDisksAtaSmartDb   *ata_smart_db;
+
         guint                    ata_smart_refresh_timer_id;
+        guint                    ata_smart_cleanup_timer_id;
 
         GList *polling_inhibitors;
 
@@ -301,6 +319,12 @@ static const DevkitDisksFilesystem known_file_systems[] = {
 
 static const int num_known_file_systems = sizeof (known_file_systems) / sizeof (DevkitDisksFilesystem);
 
+DevkitDisksAtaSmartDb *
+devkit_disks_daemon_local_get_ata_smart_db (DevkitDisksDaemon *daemon)
+{
+        return daemon->priv->ata_smart_db;
+}
+
 const DevkitDisksFilesystem *
 devkit_disks_daemon_local_get_fs_details (DevkitDisksDaemon  *daemon,
                                           const gchar        *filesystem_id)
@@ -547,10 +571,18 @@ devkit_disks_daemon_finalize (GObject *object)
                 g_object_unref (daemon->priv->mount_monitor);
         }
 
+        if (daemon->priv->ata_smart_db != NULL) {
+                g_object_unref (daemon->priv->ata_smart_db);
+        }
+
         if (daemon->priv->devkit_client != NULL) {
                 g_object_unref (daemon->priv->devkit_client);
         }
 
+        if (daemon->priv->ata_smart_cleanup_timer_id > 0) {
+                g_source_remove (daemon->priv->ata_smart_cleanup_timer_id);
+        }
+
         if (daemon->priv->ata_smart_refresh_timer_id > 0) {
                 g_source_remove (daemon->priv->ata_smart_refresh_timer_id);
         }
@@ -914,6 +946,28 @@ out:
 }
 
 static gboolean
+cleanup_ata_smart_data (DevkitDisksDaemon *daemon)
+{
+        time_t now;
+        time_t cut_off_point;
+
+        now = time (NULL);
+        cut_off_point = now - ATA_SMART_KEEP_ENTRIES_SECONDS;
+
+        g_print ("**** Deleting all ATA SMART data older than %d seconds\n", ATA_SMART_KEEP_ENTRIES_SECONDS);
+
+        devkit_disks_ata_smart_db_delete_entries (daemon->priv->ata_smart_db,
+                                                  cut_off_point);
+
+        /* cleanup in another N seconds */
+        daemon->priv->ata_smart_cleanup_timer_id = g_timeout_add_seconds (ATA_SMART_CLEANUP_INTERVAL_SECONDS,
+                                                                          (GSourceFunc) cleanup_ata_smart_data,
+                                                                          daemon);
+
+        return FALSE;
+}
+
+static gboolean
 refresh_ata_smart_data (DevkitDisksDaemon *daemon)
 {
         DevkitDisksDevice *device;
@@ -925,14 +979,15 @@ refresh_ata_smart_data (DevkitDisksDaemon *daemon)
                 if (device->priv->drive_ata_smart_is_available) {
                         char *options[] = {"nowakeup", NULL};
 
-                        g_debug ("refreshing ATA SMART data for %s", native_path);
+                        g_print ("**** Refreshing ATA SMART data for %s\n", native_path);
 
                         devkit_disks_device_drive_ata_smart_refresh_data (device, options, NULL);
                 }
         }
 
-        /* update in another 30 minutes */
-        daemon->priv->ata_smart_refresh_timer_id = g_timeout_add_seconds (30 * 60,
+
+        /* update in another N seconds */
+        daemon->priv->ata_smart_refresh_timer_id = g_timeout_add_seconds (ATA_SMART_REFRESH_INTERVAL_SECONDS,
                                                                           (GSourceFunc) refresh_ata_smart_data,
                                                                           daemon);
 
@@ -1050,6 +1105,8 @@ register_disks_daemon (DevkitDisksDaemon *daemon)
         g_signal_connect (daemon->priv->mount_monitor, "mount-added", (GCallback) mount_added, daemon);
         g_signal_connect (daemon->priv->mount_monitor, "mount-removed", (GCallback) mount_removed, daemon);
 
+        daemon->priv->ata_smart_db = devkit_disks_ata_smart_db_new ();
+
         return TRUE;
 error:
         return FALSE;
@@ -1107,8 +1164,13 @@ devkit_disks_daemon_new (void)
         devkit_disks_mount_file_clean_stale (l);
         g_list_free (l);
 
-        /* set up timer for ATA smart data refresh */
-        daemon->priv->ata_smart_refresh_timer_id = g_timeout_add_seconds (30 * 60,
+        /* clean up old ATA SMART data from the database */
+        cleanup_ata_smart_data (daemon);
+
+        /* set up timer for refreshing ATA SMART data - we don't want to refresh immediately because
+         * when adding a device we also do this...
+         */
+        daemon->priv->ata_smart_refresh_timer_id = g_timeout_add_seconds (ATA_SMART_REFRESH_INTERVAL_SECONDS,
                                                                           (GSourceFunc) refresh_ata_smart_data,
                                                                           daemon);
 
index e631532..8a037d0 100644 (file)
@@ -87,6 +87,8 @@ DevkitDisksDevice *devkit_disks_daemon_local_find_by_device_file (DevkitDisksDae
 DevkitDisksDevice *devkit_disks_daemon_local_find_by_dev         (DevkitDisksDaemon       *daemon,
                                                                   dev_t                    dev);
 
+DevkitDisksAtaSmartDb *devkit_disks_daemon_local_get_ata_smart_db (DevkitDisksDaemon       *daemon);
+
 
 PolKitCaller      *devkit_disks_damon_local_get_caller_for_context (DevkitDisksDaemon     *daemon,
                                                                     DBusGMethodInvocation *context);
index 00f9060..87f877c 100644 (file)
@@ -48,7 +48,7 @@
 #include <dbus/dbus-glib-lowlevel.h>
 #include <devkit-gobject/devkit-gobject.h>
 #include <polkit-dbus/polkit-dbus.h>
-#include <sqlite3.h>
+#include <atasmart.h>
 
 #include "devkit-disks-daemon.h"
 #include "devkit-disks-device.h"
@@ -59,6 +59,7 @@
 #include "devkit-disks-mount-file.h"
 #include "devkit-disks-inhibitor.h"
 #include "devkit-disks-poller.h"
+#include "devkit-disks-ata-smart-db.h"
 
 /*--------------------------------------------------------------------------------------------------------------*/
 #include "devkit-disks-device-glue.h"
@@ -7008,108 +7009,6 @@ 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;
 } DriveRefreshAtaSmartDataData;
@@ -7129,6 +7028,57 @@ drive_ata_smart_refresh_data_unref (DriveRefreshAtaSmartDataData *data)
         g_free (data);
 }
 
+typedef struct
+{
+        GPtrArray *attributes;
+        gboolean has_bad_attributes;
+
+} AtaSmartCollectAttrsData;
+
+static void
+ata_smart_collect_attrs (SkDisk *d, const SkSmartAttributeParsedData *a, void *user_data)
+{
+        AtaSmartCollectAttrsData *data = user_data;
+        GValue elem = {0};
+        GArray *raw_data;
+
+        raw_data = g_array_new (FALSE, TRUE, sizeof (guchar));
+        g_array_append_val (raw_data, a->raw[0]);
+        g_array_append_val (raw_data, a->raw[1]);
+        g_array_append_val (raw_data, a->raw[2]);
+        g_array_append_val (raw_data, a->raw[3]);
+        g_array_append_val (raw_data, a->raw[4]);
+        g_array_append_val (raw_data, a->raw[5]);
+
+        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, a->id,
+                                1, a->name,
+                                2, a->flags,
+                                3, a->online,
+                                4, a->prefailure,
+                                5, a->current_value,
+                                6, a->current_value_valid,
+                                7, a->worst_value,
+                                8, a->worst_value_valid,
+                                9, a->threshold,
+                                10, a->threshold_valid,
+                                11, a->good,
+                                12, a->good_valid,
+                                13, a->pretty_unit,
+                                14, a->pretty_value,
+                                15, raw_data,
+                                G_MAXUINT);
+
+        if (!a->good)
+                data->has_bad_attributes = TRUE;
+
+        g_ptr_array_add (data->attributes, g_value_get_boxed (&elem));
+
+        g_array_free (raw_data, TRUE);
+}
+
 /* may be called with context==NULL */
 static void
 drive_ata_smart_refresh_data_completed_cb (DBusGMethodInvocation *context,
@@ -7142,32 +7092,25 @@ drive_ata_smart_refresh_data_completed_cb (DBusGMethodInvocation *context,
 {
         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;
+        SkBool good;
+        uint64_t num_bad_sectors;
+        uint64_t temperature_mkelvin;
+        uint64_t power_on_mseconds;
+        const SkSmartParsedData *asd;
+        SkDisk *d;
+        guchar *blob;
+        gsize blob_size;
+        time_t time_collected;
+        gboolean is_failing;
+        gboolean is_failing_valid;
+        AtaSmartCollectAttrsData collect_attrs_data;
+        DevkitDisksAtaSmartDb *db;
 
         data = user_data;
 
+        d = NULL;
+        blob = NULL;
+
         if (job_was_cancelled || stdout == NULL) {
                 if (job_was_cancelled) {
                         if (context != NULL)
@@ -7188,219 +7131,147 @@ drive_ata_smart_refresh_data_completed_cb (DBusGMethodInvocation *context,
 
         if (rc != 0) {
                 if (rc == 2) {
-                        throw_error (context,
-                                     DEVKIT_DISKS_ERROR_ATA_SMART_WOULD_WAKEUP,
-                                     "Error retrieving S.M.A.R.T. data: %s",
-                                     stderr);
+                        if (context != NULL) {
+                                throw_error (context,
+                                             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: helper failed with exit code %d: %s",
-                                     rc, stderr);
+                        if (context != NULL) {
+                                throw_error (context,
+                                             DEVKIT_DISKS_ERROR_FAILED,
+                                             "Error retrieving S.M.A.R.T. data: helper failed with exit code %d: %s",
+                                             rc, stderr);
+                        }
                 }
                 goto out;
         }
 
-        g_print ("**************************************************\n");
-        g_print ("TODO: %s parse '%s'\n", device->priv->device_file, stdout);
-        g_print ("**************************************************\n");
-
-        tokens = g_strsplit (stdout, "|", 0);
+        blob = g_base64_decode (stdout, &blob_size);
 
-        if (g_strv_length (tokens) < 4) {
-                if (context != NULL)
+        if (sk_disk_open (NULL, &d) != 0) {
+                if (context != NULL) {
                         throw_error (context,
                                      DEVKIT_DISKS_ERROR_FAILED,
-                                     "malformed data '%s'", stdout);
+                                     "unable to open a SkDisk");
+                }
                 goto out;
         }
 
-        if (sscanf (tokens[0],
-                    "%" G_GUINT64_FORMAT,
-                    &ata_smart_time_collected) != 1) {
-                if (context != NULL)
+        if (sk_disk_set_blob (d, blob, blob_size) != 0) {
+                if (context != NULL) {
                         throw_error (context,
                                      DEVKIT_DISKS_ERROR_FAILED,
-                                     "error parsing section 0 '%s'", tokens[0]);
+                                     "error parsing blob: %s",
+                                     strerror (errno));
+                }
                 goto out;
         }
 
-        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 (sk_disk_smart_status (d, &good) != 0) {
+                is_failing = FALSE;
+                is_failing_valid = FALSE;
+        } else {
+                is_failing = !good;
+                is_failing_valid = TRUE;
         }
+        devkit_disks_device_set_drive_ata_smart_is_failing (device, is_failing);
+        devkit_disks_device_set_drive_ata_smart_is_failing_valid (device, is_failing_valid);
 
-        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 (sk_disk_smart_get_bad (d, &num_bad_sectors) != 0) {
+                num_bad_sectors = 0;
+        }
+        devkit_disks_device_set_drive_ata_smart_has_bad_sectors (device, (num_bad_sectors > 0));
+
+        time_collected = time (NULL);
+        devkit_disks_device_set_drive_ata_smart_time_collected (device, time_collected);
+
+        if (sk_disk_smart_get_temperature (d, &temperature_mkelvin) != 0) {
+                temperature_mkelvin = 0;
+        }
+        devkit_disks_device_set_drive_ata_smart_temperature_kelvin (device, temperature_mkelvin / 1000.0);
+
+        if (sk_disk_smart_get_power_on (d, &power_on_mseconds) != 0) {
+                power_on_mseconds = 0;
+        }
+        devkit_disks_device_set_drive_ata_smart_power_on_seconds (device, power_on_mseconds / 1000);
+
+        if (sk_disk_smart_parse (d, &asd) != 0) {
+                devkit_disks_device_set_drive_ata_smart_offline_data_collection_status (device, 0);
+                devkit_disks_device_set_drive_ata_smart_offline_data_collection_seconds (device, 0);
+                devkit_disks_device_set_drive_ata_smart_self_test_execution_status (device, 0);
+                devkit_disks_device_set_drive_ata_smart_self_test_execution_percent_remaining (device, 0);
+                devkit_disks_device_set_drive_ata_smart_short_and_extended_self_test_available (device, FALSE);
+                devkit_disks_device_set_drive_ata_smart_conveyance_self_test_available (device, FALSE);
+                devkit_disks_device_set_drive_ata_smart_start_self_test_available (device, FALSE);
+                devkit_disks_device_set_drive_ata_smart_abort_self_test_available (device, FALSE);
+                devkit_disks_device_set_drive_ata_smart_short_self_test_polling_minutes (device, 0);
+                devkit_disks_device_set_drive_ata_smart_extended_self_test_polling_minutes (device, 0);
+                devkit_disks_device_set_drive_ata_smart_conveyance_self_test_polling_minutes (device, 0);
+        } else {
+                devkit_disks_device_set_drive_ata_smart_offline_data_collection_status
+                        (device, asd->offline_data_collection_status);
+                devkit_disks_device_set_drive_ata_smart_offline_data_collection_seconds
+                        (device, asd->total_offline_data_collection_seconds);
+                devkit_disks_device_set_drive_ata_smart_self_test_execution_status
+                        (device, asd->self_test_execution_status);
+                devkit_disks_device_set_drive_ata_smart_self_test_execution_percent_remaining
+                        (device, asd->self_test_execution_percent_remaining);
+                devkit_disks_device_set_drive_ata_smart_short_and_extended_self_test_available
+                        (device, asd->short_and_extended_test_available);
+                devkit_disks_device_set_drive_ata_smart_conveyance_self_test_available
+                        (device, asd->conveyance_test_available);
+                devkit_disks_device_set_drive_ata_smart_start_self_test_available
+                        (device, asd->start_test_available);
+                devkit_disks_device_set_drive_ata_smart_abort_self_test_available
+                        (device, asd->abort_test_available);
+                devkit_disks_device_set_drive_ata_smart_short_self_test_polling_minutes
+                        (device, asd->short_test_polling_minutes);
+                devkit_disks_device_set_drive_ata_smart_extended_self_test_polling_minutes
+                        (device, asd->extended_test_polling_minutes);
+                devkit_disks_device_set_drive_ata_smart_conveyance_self_test_polling_minutes
+                        (device, asd->conveyance_test_polling_minutes);
+        }
+
+        collect_attrs_data.attributes = g_ptr_array_new ();
+        collect_attrs_data.has_bad_attributes = FALSE;
+        if (sk_disk_smart_parse_attributes (d, ata_smart_collect_attrs, &collect_attrs_data) != 0) {
                 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};
-
-                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;
-                }
-
-                g_ptr_array_add (attributes, g_value_get_boxed (&elem));
+                                     "Error parsing ATA SMART attributes: %m");
+                g_ptr_array_free (collect_attrs_data.attributes, TRUE);
+                goto out;
         }
 
-        devkit_disks_device_set_drive_ata_smart_attributes_steal (device, attributes);
+        devkit_disks_device_set_drive_ata_smart_has_bad_attributes (device, collect_attrs_data.has_bad_attributes);
+        devkit_disks_device_set_drive_ata_smart_attributes_steal (device, collect_attrs_data.attributes);
 
         /* emit change event since we've updated the smart data */
         drain_pending_changes (device, FALSE);
 
-        /* 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;
-                        }
-                }
-
-                now = time (NULL);
-
-                s = g_string_new (NULL);
-                if (contents != NULL) {
-                        gchar **lines;
-
-                        lines = g_strsplit (contents, "\n", 0);
-                        for (n = 0; lines[n] != NULL; n++) {
-                                time_t time_collected;
-
-                                time_collected = (time_t) atoll (lines[n]);
-
-                                /* keep data around for 7 days */
-                                if (now - time_collected > 7 * 24 * 60 * 60)
-                                        continue;
-
-                                g_string_append (s, lines[n]);
-                                g_string_append_c (s, '\n');
-                        }
-                        g_strfreev (lines);
-
-                        g_free (contents);
-                }
-
-                g_string_append (s, stdout);
-
-                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;
-                }
-
-                g_free (filename);
-        }
-
         if (context != NULL)
                 dbus_g_method_return (context);
+
+        /* store the (time_collected, disk_id, blob) tupple in our database */
+        db = devkit_disks_daemon_local_get_ata_smart_db (device->priv->daemon);
+        devkit_disks_ata_smart_db_add_entry (db,
+                                             device,
+                                             time_collected,
+                                             is_failing,
+                                             is_failing_valid,
+                                             (num_bad_sectors > 0),
+                                             collect_attrs_data.has_bad_attributes,
+                                             temperature_mkelvin / 1000.0,
+                                             power_on_mseconds / 1000,
+                                             blob,
+                                             blob_size);
+
 out:
-        g_strfreev (tokens);
+        g_free (blob);
+        if (d != NULL)
+                sk_disk_free (d);
 }
 
 /* may be called with context==NULL */
@@ -7498,20 +7369,77 @@ out:
 
 /*--------------------------------------------------------------------------------------------------------------*/
 
+static gboolean
+ata_smart_historical_data_cb (time_t      time_collected,
+                              gboolean    is_failing,
+                              gboolean    is_failing_valid,
+                              gboolean    has_bad_sectors,
+                              gboolean    has_bad_attributes,
+                              gdouble     temperature_kelvin,
+                              guint64     power_on_seconds,
+                              const void *blob,
+                              gsize       blob_size,
+                              gpointer    user_data)
+{
+        GPtrArray *array = user_data;
+        GValue elem = {0};
+        SkDisk *d;
+        AtaSmartCollectAttrsData collect_attrs_data;
+
+        d = NULL;
+
+        if (sk_disk_open (NULL, &d) != 0) {
+                g_warning ("Unable to open a SkDisk");
+                goto out;
+        }
+
+        if (sk_disk_set_blob (d, blob, blob_size) != 0) {
+                g_warning ("Error parsing blob: %s", strerror (errno));
+                goto out;
+        }
+
+        collect_attrs_data.attributes = g_ptr_array_new ();
+        collect_attrs_data.has_bad_attributes = FALSE;
+        if (sk_disk_smart_parse_attributes (d, ata_smart_collect_attrs, &collect_attrs_data) != 0) {
+                g_warning ("Error parsing ATA SMART attributes: %m");
+        }
+
+        g_value_init (&elem, ATA_SMART_HISTORICAL_SMART_DATA_STRUCT_TYPE);
+        g_value_take_boxed (&elem, dbus_g_type_specialized_construct (ATA_SMART_HISTORICAL_SMART_DATA_STRUCT_TYPE));
+        dbus_g_type_struct_set (&elem,
+                                0, time_collected,
+                                1, is_failing,
+                                2, is_failing_valid,
+                                3, has_bad_sectors,
+                                4, has_bad_attributes,
+                                5, temperature_kelvin,
+                                6, power_on_seconds,
+                                7, collect_attrs_data.attributes,
+                                G_MAXUINT);
+
+        g_ptr_array_foreach (collect_attrs_data.attributes, (GFunc) g_value_array_free, NULL);
+        g_ptr_array_free (collect_attrs_data.attributes, TRUE);
+
+        g_ptr_array_add (array, g_value_get_boxed (&elem));
+
+ out:
+        if (d != NULL)
+                sk_disk_free (d);
+        return FALSE;
+}
+
 gboolean
 devkit_disks_device_drive_ata_smart_get_historical_data (DevkitDisksDevice     *device,
-                                                         guint64                from,
-                                                         guint64                to,
+                                                         guint64                since,
+                                                         guint64                until,
+                                                         guint64                spacing,
                                                          DBusGMethodInvocation *context)
 {
         PolKitCaller *pk_caller;
         GPtrArray *array;
-        gchar *filename;
-        gchar *contents;
-        GError *error;
+        DevkitDisksAtaSmartDb *db;
 
         pk_caller = NULL;
-        filename = NULL;
 
         if (context != NULL) {
                 if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon,
@@ -7536,133 +7464,25 @@ devkit_disks_device_drive_ata_smart_get_historical_data (DevkitDisksDevice     *
                 }
         }
 
-        if (from > to) {
-                throw_error (context, DEVKIT_DISKS_ERROR_FAILED, "Malformed time range (from > to)");
-                goto out;
-        }
-
-        to = time (NULL);
+        if (until == 0)
+                until = 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");
+        if (since > until) {
+                throw_error (context, DEVKIT_DISKS_ERROR_FAILED, "Malformed time range (since > until)");
                 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;
-                }
-        }
+        db = devkit_disks_daemon_local_get_ata_smart_db (device->priv->daemon);
 
         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);
-        }
-
+        devkit_disks_ata_smart_db_get_entries (db,
+                                               device,
+                                               since,
+                                               until,
+                                               spacing,
+                                               ata_smart_historical_data_cb,
+                                               array);
 
         dbus_g_method_return (context, array);
 
@@ -7670,7 +7490,6 @@ devkit_disks_device_drive_ata_smart_get_historical_data (DevkitDisksDevice     *
         g_ptr_array_free (array, TRUE);
 
 out:
-        g_free (filename);
         if (pk_caller != NULL)
                 polkit_caller_unref (pk_caller);
         return TRUE;
index dcb8ab2..61bd6e9 100644 (file)
@@ -148,8 +148,9 @@ gboolean devkit_disks_device_drive_ata_smart_refresh_data (DevkitDisksDevice
                                                            DBusGMethodInvocation *context);
 
 gboolean devkit_disks_device_drive_ata_smart_get_historical_data (DevkitDisksDevice     *device,
-                                                                  guint64                from,
-                                                                  guint64                to,
+                                                                  guint64                since,
+                                                                  guint64                until,
+                                                                  guint64                spacing,
                                                                   DBusGMethodInvocation *context);
 
 gboolean devkit_disks_device_drive_ata_smart_initiate_selftest (DevkitDisksDevice     *device,
index 1f29ea9..d472e04 100644 (file)
@@ -30,6 +30,7 @@ typedef struct DevkitDisksDevice       DevkitDisksDevice;
 typedef struct DevkitDisksMount        DevkitDisksMount;
 typedef struct DevkitDisksMountMonitor DevkitDisksMountMonitor;
 typedef struct DevkitDisksInhibitor    DevkitDisksInhibitor;
+typedef struct DevkitDisksAtaSmartDb   DevkitDisksAtaSmartDb;
 
 G_END_DECLS
 
index 6a61e55..dd7a526 100644 (file)
@@ -33,73 +33,6 @@ 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[])
 {
@@ -108,19 +41,14 @@ main (int argc, char *argv[])
         SkDisk *d;
         SkBool smart_is_available;
         SkBool awake;
-        SkBool good;
-        const SkSmartParsedData *data;
-        GString *s;
-        GList *attrs;
-        GList *l;
         gboolean nowakeup;
+        const void *blob;
+        size_t blob_size;
+        gchar *encoded_blob;
 
         d = NULL;
-        attrs = NULL;
         ret = 1;
 
-        s = g_string_new (NULL);
-
         if (argc != 3) {
                 usage ();
                 goto out;
@@ -131,100 +59,45 @@ main (int argc, char *argv[])
         nowakeup = atoi (argv[2]);
 
         if (sk_disk_open (device, &d) != 0) {
-                fprintf (stderr, "Failed to open disk %s: %s\n", device, strerror (errno));
+                g_printerr ("Failed to open disk %s: %m\n", device);
                 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));
+                g_printerr ("Failed to check if disk %s is awake: %m\n", device);
                 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);
+                g_printerr ("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));
+                g_printerr ("Failed to determine if smart is available for %s: %m\n", device);
                 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));
+                g_printerr ("Failed to read smart data for %s: %m\n", device);
                 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));
+        if (sk_disk_get_blob (d, &blob, &blob_size) != 0) {
+                g_printerr ("Failed to read smart data for %s: %m\n", device);
                 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);
+        encoded_blob = g_base64_encode ((const guchar *) blob, (gsize) blob_size);
+        g_print ("%s\n", encoded_blob);
+        g_free (encoded_blob);
 
         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);
index 2f26114..0e70eaa 100644 (file)
             passed the current time will be used.
         </doc:summary></doc:doc>
       </arg>
+      <arg name="spacing" direction="in" type="t">
+        <doc:doc><doc:summary>
+            The minimum spacing (in seconds) between two data points or 0
+            to get all data points between @since and @until.
+        </doc:summary></doc:doc>
+      </arg>
       <arg name="data" direction="out" type="a(tbbbbdta(usubbybybybbbutay))">
         <doc:doc><doc:summary>
-            An array of historical data. Each element contains
+            An array of historical data sorted by collection date. Each element contains
             the following members (TODO).
         </doc:summary></doc:doc>
       </arg>