From f89ed71af80ebd54935cf14f01602ff73e8d8992 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Jul 2008 18:57:23 +0200 Subject: lots of updates --- gnome-disk-health.glade | 301 ++++++++++++++++++++++++++---------------------- gnome-disk-health.vala | 246 +++++++++++++++++++++++++++++++++++++-- smartkitd.vala | 49 ++++++-- 3 files changed, 443 insertions(+), 153 deletions(-) diff --git a/gnome-disk-health.glade b/gnome-disk-health.glade index c345aba..a1a122f 100644 --- a/gnome-disk-health.glade +++ b/gnome-disk-health.glade @@ -1,6 +1,6 @@ - + 5 @@ -48,54 +48,66 @@ 6 6 - + True - 1 - <b>Path:</b> - True + 0 + label - GTK_FILL - GTK_FILL + 1 + 2 + 4 + 5 - + True - 1 - <b>Size:</b> - True + 0 + label - 1 - 2 - GTK_FILL + 1 + 2 + 3 + 4 - + True - 1 - <b>Model:</b> - True + 0 + label + 1 + 2 2 3 - GTK_FILL - + True - 1 - <b>Serial Number:</b> - True + 0 + label - 3 - 4 - GTK_FILL + 1 + 2 + 1 + 2 + + + + + True + 0 + label + + + 1 + 2 @@ -112,66 +124,54 @@ - - True - 0 - label - - - 1 - 2 - - - - + True - 0 - label + 1 + <b>Serial Number:</b> + True - 1 - 2 - 1 - 2 + 3 + 4 + GTK_FILL - + True - 0 - label + 1 + <b>Model:</b> + True - 1 - 2 2 3 + GTK_FILL - + True - 0 - label + 1 + <b>Size:</b> + True - 1 - 2 - 3 - 4 + 1 + 2 + GTK_FILL - + True - 0 - label + 1 + <b>Path:</b> + True - 1 - 2 - 4 - 5 + GTK_FILL + GTK_FILL @@ -236,6 +236,16 @@ 3 + + + True + The disk has been powered on for 3.1 months. + + + False + 4 + + 16 @@ -299,67 +309,58 @@ 6 6 - - True - 1 - <b>Name:</b> - True - - - GTK_FILL - GTK_FILL - - - - + True - 1 - <b>ID:</b> - True + 0 + n/a - 1 - 2 + 1 + 2 + 6 + 7 GTK_FILL GTK_FILL - + True 1 - <b>Current Value:</b> + <b>Verdict:</b> True - 2 - 3 + 6 + 7 GTK_FILL GTK_FILL - + True - 1 - <b>Worst Value:</b> - True + 0 + n/a - 3 - 4 + 1 + 2 + 5 + 6 GTK_FILL GTK_FILL - + True - 1 - <b>Threshold:</b> - True + 0 + n/a + 1 + 2 4 5 GTK_FILL @@ -367,28 +368,31 @@ - + True - 1 - <b>Type:</b> - True + 0 + n/a - 5 - 6 + 1 + 2 + 3 + 4 GTK_FILL GTK_FILL - + True 0 - label + n/a 1 2 + 2 + 3 GTK_FILL GTK_FILL @@ -397,7 +401,7 @@ True 0 - label + n/a 1 @@ -409,44 +413,40 @@ - + True 0 - label + n/a 1 2 - 2 - 3 GTK_FILL GTK_FILL - + True - 0 - label + 1 + <b>Type:</b> + True - 1 - 2 - 3 - 4 + 5 + 6 GTK_FILL GTK_FILL - + True - 0 - label + 1 + <b>Threshold:</b> + True - 1 - 2 4 5 GTK_FILL @@ -454,45 +454,55 @@ - + True - 0 - label + 1 + <b>Worst Value:</b> + True - 1 - 2 - 5 - 6 + 3 + 4 GTK_FILL GTK_FILL - + True 1 - <b>Verdict:</b> + <b>Current Value:</b> True - 6 - 7 + 2 + 3 GTK_FILL GTK_FILL - + True - 0 - label + 1 + <b>ID:</b> + True + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 1 + <b>Name:</b> + True - 1 - 2 - 6 - 7 GTK_FILL GTK_FILL @@ -598,7 +608,17 @@ True - GTK_BUTTONBOX_END + GTK_BUTTONBOX_EDGE + + + True + True + True + gtk-refresh + True + 0 + + True @@ -608,6 +628,9 @@ True 0 + + 1 + diff --git a/gnome-disk-health.vala b/gnome-disk-health.vala index 2632289..a3fcd4d 100644 --- a/gnome-disk-health.vala +++ b/gnome-disk-health.vala @@ -24,26 +24,51 @@ using DBus; using GLib; using Gtk; + +public DiskHealth disk_health; + public class DiskHealth : Gtk.Builder { string uifile = "gnome-disk-health.ui"; public bool create_widgets(string disk_string) { + test = null; + try { add_from_file (uifile); Gtk.Widget window = (Gtk.Widget) get_object("DiskHealthDialog"); + Gtk.TreeView tree_view = (Gtk.TreeView) get_object ("attributeTreeView"); + + /* id, name, text, icon, current, worst, threshold, type, verdict */ + list_store = new Gtk.ListStore(9, typeof(uint8), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string)); + + tree_view.set_model(list_store); + + tree_view.insert_column_with_attributes (-1, null, new Gtk.CellRendererPixbuf(), "icon-name", 3, null); + tree_view.insert_column_with_attributes(-1, "Name", new Gtk.CellRendererText(), "text", 1, null); + + Gtk.CellRenderer cell = new Gtk.CellRendererText(); + cell.set("xalign", 1.0, null); + tree_view.insert_column_with_attributes (-1, "Value", cell, "text", 2, null); + if (!setup_connection(disk_string)) return false; if (!fill_in_data()) return false; + setup_refresh_timer(); + window.show_all(); window.destroy += Gtk.main_quit; ((Gtk.Button) get_object("closeButton")).clicked += Gtk.main_quit; + ((Gtk.Button) get_object("refreshButton")).clicked += refresh; + + ((Gtk.Button) get_object("selfTestButton")).clicked += selfTest; + } catch (GLib.Error e) { stderr.printf("Failed to create main window: %s\n", e.message); return false; @@ -56,6 +81,8 @@ public class DiskHealth : Gtk.Builder { private dynamic DBus.Object manager; private DBus.ObjectPath dbus_path; private dynamic DBus.Object disk; + private Gtk.ListStore list_store; + private string test; public bool setup_connection(string disk_string) { try { @@ -86,6 +113,58 @@ public class DiskHealth : Gtk.Builder { return true; } + public void refresh() { + try { + disk.readSmartData(true); + fill_in_data(); + setup_refresh_timer(); + } catch (DBus.Error e) { + stderr.printf("D-Bus error: %s\n", e.message); + } + } + + public uint refresh_time; + public uint refresh_timer = 0; + + public void setup_refresh_timer() { + + uint t; + + if (refresh_time <= 0) + t = 5*60; + else if (refresh_time == 1) + t = 5; /* if 1 min is request, we do 10s + * instead, since we know that + * the minimal value is 1min */ + else + t = refresh_time*60; + + if (t > 5*60) + t = 5*60; + + stderr.printf("Setting wakeup to %u (%u)\n", t, refresh_time); + + disk_health = this; + + if (refresh_timer > 0) + Source.remove(refresh_timer); + refresh_timer = Timeout.add_seconds(t, + () => { + stderr.printf("Woke up\n"); + disk_health.refresh(); + return false; + }); + } + + public void selfTest() { + try { + disk.startSelfTest(test); + refresh(); + } catch (DBus.Error e) { + stderr.printf("D-Bus error: %s\n", e.message); + } + } + public bool fill_in_data() { try { @@ -124,10 +203,47 @@ public class DiskHealth : Gtk.Builder { ((Gtk.Label) get_object("healthLabel")).set_label(""); ((Gtk.Label) get_object("badSectorsLabel")).set_label(""); ((Gtk.Label) get_object("temperatureLabel")).set_label(""); + ((Gtk.Label) get_object("powerOnLabel")).set_label(""); return fill_in_no_self_test_data(); } + public string format_pretty(uint64 value, string unit) { + + switch (unit) { + + case "mseconds": + + if (value >= (uint64)1000*60*60*24*365) + return "%0.1f years".printf(((double) value)/(1000.0*60*60*24*365)); + else if (value >= (uint64) 1000*60*60*24*30) + return "%0.1f months".printf(((double) value)/(1000.0*60*60*24*30)); + else if (value >= 1000*60*60*24) + return "%0.1f days".printf(((double) value)/(1000.0*60*60*24)); + else if (value >= 1000*60*60) + return "%0.1f h".printf(((double) value)/(1000.0*60*60)); + else if (value >= 1000*60) + return "%0.1f min".printf(((double) value)/(1000.0*60)); + else if (value >= 1000) + return "%0.1f s".printf(((double) value)/(1000.0)); + else + return "%llu ms".printf(value); + + case "mkelvin": + return "%0.1f °C".printf(((double) value - 273150) / 1000); + + case "sectors": + return "%llu sectors".printf(value); + + case "none": + return "%llu".printf(value); + + } + + return "n/a"; + + } + public bool fill_in_smart_data() { try { @@ -136,16 +252,103 @@ public class DiskHealth : Gtk.Builder { bool b = disk.checkSmartStatus(); if (b) - ((Gtk.Label) get_object("healthLabel")).set_label("Disk reports to be healthy."); + ((Gtk.Label) get_object("healthLabel")).set_label("The disk reports to be healthy."); + else + ((Gtk.Label) get_object("healthLabel")).set_label("The disk reports that it has already failed or is expected to fail in the next 24h."); + + DBus.ObjectPath[] attrs = disk.getAttributes(); + + uint64 temp = 0; + uint64 rsc = 0; + uint64 rec = 0; + uint64 cps = 0; + uint64 ou = 0; + uint64 pon = 0; + + list_store.clear(); + + foreach (DBus.ObjectPath a in attrs) { + dynamic DBus.Object attribute; + uint8 id; + bool good, online, prefailure; + string name; + uint64 pretty_value; + string pretty_unit; + uint8 current, worst, threshold; + + attribute = connection.get_object("net.poettering.SmartKit", a, "net.poettering.SmartKit.Attribute"); + + /* id, name, text, icon, current, worst, threshold, type, verdict */ + + id = attribute.getId(); + name = attribute.getName(); + pretty_value = attribute.getPrettyValue(); + pretty_unit = attribute.getPrettyUnit(); + good = attribute.isGood(); + current = attribute.getCurrentValue(); + worst = attribute.getWorstValue(); + threshold = attribute.getThreshold(); + online = attribute.isOnline(); + prefailure = attribute.isPrefailure(); + + Gtk.TreeIter iter; + list_store.append(out iter); + list_store.set(iter, + 0, id, + 1, name, + 2, format_pretty(pretty_value, pretty_unit), + 3, good ? "dialog-ok" : "dialog-warning", + 4, "%u".printf(current), + 5, "%u".printf(worst), + 6, "%u".printf(threshold), + 7, "%s/%s".printf(online ? "Online" : "Offline", prefailure ? "Prefailure" : "Old Age"), + 8, good ? "OK" : "FAILURE", + -1); + + if (name == "reallocated-sector-count") + rsc = pretty_value; + if (name == "reallocated-event-count") + rec = pretty_value; + if (name == "current-pending-sector") + cps = pretty_value; + if (name == "offline-uncorrectable") + ou = pretty_value; + + if (name == "temperature-celsius-1" || + name == "temperature-celsius-2" || + name == "airflow-temperature-celsius") + if (pretty_value > temp) + temp = pretty_value; + + if (name == "power-on-minutes" || + name == "power-on-seconds" || + name == "power-on-hours") + pon = pretty_value; + } + + uint64 sectors = cps + ou + (rsc > rec ? rsc : rec); + if (sectors > 0) + ((Gtk.Label) get_object("badSectorsLabel")).set_markup("Warning: The disk reports to have %llu bad sectors.".printf(sectors)); + else + ((Gtk.Label) get_object("badSectorsLabel")).set_label("The disk reports to have no bad sectors."); + + if (temp > 0) + ((Gtk.Label) get_object("temperatureLabel")).set_label("The current temperature of the disk is %0.1f °C.".printf((double) (temp - 273150) / 1000)); else - ((Gtk.Label) get_object("healthLabel")).set_label("Disk reports that it has already failed or is expected to fail in the next 24h."); + ((Gtk.Label) get_object("temperatureLabel")).set_label(""); + + + if (pon > 0) + ((Gtk.Label) get_object("powerOnLabel")).set_label("The disk has been powered on for %s.".printf(format_pretty(pon, "mseconds"))); + else + ((Gtk.Label) get_object("powerOnLabel")).set_label(""); bool a = disk.getStartTestAvailable(); b = disk.getConveyanceTestAvailable(); bool c = disk.getShortAndExtendedTestAvailable(); if (a && (b || c)) - return fill_in_self_test_data(); + return fill_in_self_test_data(c, b); else return fill_in_no_self_test_data(); @@ -155,7 +358,7 @@ public class DiskHealth : Gtk.Builder { } } - public bool fill_in_self_test_data() { + public bool fill_in_self_test_data(bool sae, bool c) { try { bool show_percent = false; string text; @@ -191,7 +394,7 @@ public class DiskHealth : Gtk.Builder { text = "The previous self-test completed having a test element that failed and the device is suspected of having handling damage."; break; case "inprogress": - text = "Self-test in progres..."; + text = "Self-test in progress..."; show_percent = true; break; @@ -200,7 +403,6 @@ public class DiskHealth : Gtk.Builder { break; } - ((Gtk.Label) get_object("selfTestLabel")).set_label(text); if (show_percent) { @@ -208,11 +410,40 @@ public class DiskHealth : Gtk.Builder { ((Gtk.ProgressBar) get_object("selfTestProgressBar")).set_fraction((double) (100-percent)/100); ((Gtk.ProgressBar) get_object("selfTestProgressBar")).set_text("%u%% remaining".printf(percent)); - } else + } else { + ((Gtk.ProgressBar) get_object("selfTestProgressBar")).set_fraction(0.0); ((Gtk.ProgressBar) get_object("selfTestProgressBar")).set_text("n/a"); + } ((Gtk.ProgressBar) get_object("selfTestProgressBar")).set_sensitive(show_percent); + if (state == "inprogress") { + ((Gtk.Button) get_object("selfTestButton")).set_label("Abort Self-Test"); + bool b = disk.getAbortTestAvailable(); + ((Gtk.Button) get_object("selfTestButton")).set_sensitive(b); + + this.test = "abort"; + + if (sae) + this.refresh_time = disk.getShortTestPollingMinutes(); + else + this.refresh_time = disk.getConveyanceTestPollingMinutes(); + + + } else { + uint saet, ct; + + ((Gtk.Button) get_object("selfTestButton")).set_label("Start Self-Test"); + ((Gtk.Button) get_object("selfTestButton")).set_sensitive(true); + + if (sae) + this.test = "short"; + else + this.test = "conveyance"; + + this.refresh_time = 0; + } + } catch (DBus.Error e) { stderr.printf("D-Bus error: %s\n", e.message); return false; @@ -226,6 +457,7 @@ public class DiskHealth : Gtk.Builder { ((Gtk.Button) get_object("selfTestButton")).set_sensitive(false); ((Gtk.ProgressBar) get_object("selfTestProgressBar")).set_sensitive(false); + test = null; return true; } diff --git a/smartkitd.vala b/smartkitd.vala index 7e60403..1cbc41e 100644 --- a/smartkitd.vala +++ b/smartkitd.vala @@ -85,13 +85,14 @@ public interface DiskAPI { public abstract uint getExtendedTestPollingMinutes() throws Error; public abstract uint getConveyanceTestPollingMinutes() throws Error; -/* public abstract DBus.ObjectPath[] getAttributes() throws Error; */ + public abstract DBus.ObjectPath[] getAttributes() throws Error; } [DBus (name = "net.poettering.SmartKit.Manager")] public interface ManagerAPI { public abstract DBus.ObjectPath getDiskByUDI(string udi) throws Error; public abstract DBus.ObjectPath getDiskByPath(string path) throws Error; + public abstract DBus.ObjectPath[] getDisks() throws Error; } string clean_path(string s) { @@ -114,7 +115,7 @@ public class Attribute : GLib.Object, AttributeAPI { public Disk disk { get; construct; } public DBus.Connection connection { get; construct; } - public uint8 id; + public uint8 _id; public string name; public SmartAttributeUnit pretty_unit; public uint8 threshold; @@ -132,7 +133,7 @@ public class Attribute : GLib.Object, AttributeAPI { } public void set(SmartAttributeParsedData a) { - id = a.id; + _id = a.id; name = a.name; pretty_unit = a.pretty_unit; threshold = a.threshold; @@ -153,7 +154,7 @@ public class Attribute : GLib.Object, AttributeAPI { } public uint8 getId() throws Error { - return id; + return _id; } public string getName() throws Error { @@ -169,10 +170,12 @@ public class Attribute : GLib.Object, AttributeAPI { return "none"; case SmartAttributeUnit.MSECONDS: return "mseconds"; + case SmartAttributeUnit.SECTORS: + return "sectors"; case SmartAttributeUnit.MKELVIN: return "mkelvin"; default: - throw new Error.UNKNOWN_UNIT("Unit unknown."); + throw new Error.UNKNOWN_UNIT("Unit unknown %i.", pretty_unit); } } @@ -245,6 +248,12 @@ public class Disk : GLib.Object, DiskAPI { } private void attribute_callback(void* disk, SmartAttributeParsedData a) { + foreach (Attribute attr in attributes) + if (attr._id == a.id) { + attr.set(a); + return; + } + Attribute attr; attr = new Attribute(this, this.connection); @@ -350,9 +359,11 @@ public class Disk : GLib.Object, DiskAPI { if (this.disk.smart_read_data() < 0) throw new Error.SYSTEM("smart_read_data() failed: %s", Smart.strerror(Smart.errno)); + + populate_attributes(); } - public void startSelfTest(string test) { + public void startSelfTest(string test) throws Error { SmartSelfTest t; switch (test) { @@ -373,7 +384,7 @@ public class Disk : GLib.Object, DiskAPI { throw new Error.SYSTEM("smart_self_test() failed: %s", Smart.strerror(Smart.errno)); } - public void abortSelfTest() { + public void abortSelfTest() throws Error { if (this.disk.smart_self_test(SmartSelfTest.ABORT) < 0) throw new Error.SYSTEM("smart_self_test() failed: %s", Smart.strerror(Smart.errno)); @@ -517,6 +528,18 @@ public class Disk : GLib.Object, DiskAPI { return d->conveyance_test_polling_minutes; } + public DBus.ObjectPath[] getAttributes() throws Error { + DBus.ObjectPath[] p = new DBus.ObjectPath[attributes.length()]; + + int i = 0; + foreach (Attribute a in attributes) { + p[i++] = new DBus.ObjectPath(a.dbus_path); + } + + return p; + } + + /* public uint64 size { */ /* get { */ /* uint64 s; */ @@ -581,6 +604,18 @@ public class Manager : GLib.Object, ManagerAPI { throw new Error.NOT_FOUND("Device not found"); } + + public DBus.ObjectPath[] getDisks() throws Error { + DBus.ObjectPath[] p = new DBus.ObjectPath[disks.length()]; + + int i = 0; + foreach (Disk d in disks) { + p[i++] = new DBus.ObjectPath(d.dbus_path); + } + + return p; + } + } int main() { -- cgit