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)
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)
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)
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
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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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__ */
#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>
#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"
DevkitDisksMountMonitor *mount_monitor;
+ DevkitDisksAtaSmartDb *ata_smart_db;
+
guint ata_smart_refresh_timer_id;
+ guint ata_smart_cleanup_timer_id;
GList *polling_inhibitors;
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)
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);
}
}
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;
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);
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;
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);
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);
#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"
#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"
/*--------------------------------------------------------------------------------------------------------------*/
-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,
- ¤t, ¤t_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;
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,
{
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)
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 */
/*--------------------------------------------------------------------------------------------------------------*/
+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,
}
}
- 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);
g_ptr_array_free (array, TRUE);
out:
- g_free (filename);
if (pk_caller != NULL)
polkit_caller_unref (pk_caller);
return TRUE;
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,
typedef struct DevkitDisksMount DevkitDisksMount;
typedef struct DevkitDisksMountMonitor DevkitDisksMountMonitor;
typedef struct DevkitDisksInhibitor DevkitDisksInhibitor;
+typedef struct DevkitDisksAtaSmartDb DevkitDisksAtaSmartDb;
G_END_DECLS
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[])
{
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;
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);
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>