summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2008-06-30 21:50:56 +0200
committerLennart Poettering <lennart@poettering.net>2008-06-30 21:50:56 +0200
commitf671a647e5480a709f09a66252995bf07e512e32 (patch)
tree35e768e9bc096abe54e7a2d59c75d1aae84afca8
parent09358348c50656e7c6d508767cee3996e8a3d77f (diff)
a lot of love
-rw-r--r--.gitignore8
-rw-r--r--Makefile18
-rw-r--r--gnome-disk-health.glade620
-rw-r--r--gnome-disk-health.vala137
-rw-r--r--skdump.c52
-rw-r--r--sktest.c66
-rw-r--r--smart.c1615
-rw-r--r--smart.h181
-rw-r--r--smart.vapi139
-rw-r--r--smartkitd.vala390
10 files changed, 2436 insertions, 790 deletions
diff --git a/.gitignore b/.gitignore
index 884e119..05900f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,10 @@
skdump
+sktest
+gnome-disk-health
+gnome-disk-health.c
+gnome-disk-health.h
+gnome-disk-health.ui
+smartkitd
+smartkitd.c
+smartkitd.h
*.o
diff --git a/Makefile b/Makefile
index 6621354..687adb5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,22 @@
-CFLAGS=`pkg-config --cflags glib-2.0` -pipe -Wall -W -O0 -g
+CFLAGS=`pkg-config --cflags glib-2.0` -pipe -Wall -W -O0 -g -I.
LIBS=`pkg-config --libs glib-2.0`
+all: skdump sktest smartkitd gnome-disk-health gnome-disk-health.ui
+
skdump: smart.o skdump.o
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+sktest: smart.o sktest.o
+ $(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+
+smartkitd: smart.c smartkitd.vala
+ valac --save-temps -g -o $@ --vapidir=. --pkg=smart --pkg=hal --pkg=dbus-glib-1 --Xcc=-I. $^
+
+gnome-disk-health: gnome-disk-health.vala
+ valac --save-temps -g -o $@ --pkg=gtk+-2.0 --pkg=dbus-glib-1 $^
+
+gnome-disk-health.ui: gnome-disk-health.glade
+ gtk-builder-convert $< $@
+
clean:
- rm -f skdump *.o
+ rm -f skdump sktest *.o smartkitd gnome-disk-health gnome-disk-health.ui
diff --git a/gnome-disk-health.glade b/gnome-disk-health.glade
new file mode 100644
index 0000000..5453e84
--- /dev/null
+++ b/gnome-disk-health.glade
@@ -0,0 +1,620 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.4 on Mon Jun 30 04:28:55 2008 -->
+<glade-interface>
+ <widget class="GtkDialog" id="DiskHealthDialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Disk Health</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="icon_name">drive-harddisk</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">12</property>
+ <property name="ypad">12</property>
+ <property name="icon_size">6</property>
+ <property name="pixel_size">64</property>
+ <property name="icon_name">drive-harddisk</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="firmwareLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="serialLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="modelLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="sizeLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="pathLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Firmware:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Serial Number:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Model:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Size:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Path:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="spacing">24</property>
+ <child>
+ <widget class="GtkLabel" id="smartLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Disk health functionality available (S.M.A.R.T.)</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="healthLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Disk is healthy.</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="badSectorsLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">A few sectors have been reallocated.</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="temperatureLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Current disk temperature is 40°C.</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">16</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Summary</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="label19">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">S.M.A.R.T. Attributes:</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <widget class="GtkTreeView" id="attributeTreeView">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkExpander" id="expander1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <widget class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="n_rows">7</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="verdictLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Verdict:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="typeLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="thresholdLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="worstLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="currentLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="idLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="nameLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label26">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Type:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Threshold:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Worst Value:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Current Value:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label22">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;ID:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label21">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">&lt;b&gt;Name:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label20">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Explanation</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Details</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="spacing">16</property>
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkProgressBar" id="progressbar1">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="button2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="label" translatable="yes">Start Self-Test</property>
+ <property name="response_id">0</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Self-test functionality available. Approximate run-time is 120 min.</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Self-Test</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="button1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="label" translatable="yes">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/gnome-disk-health.vala b/gnome-disk-health.vala
new file mode 100644
index 0000000..011f7c0
--- /dev/null
+++ b/gnome-disk-health.vala
@@ -0,0 +1,137 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ This file is part of SmartKit.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+using DBus;
+using GLib;
+using Gtk;
+
+public class DiskHealth : Gtk.Builder {
+
+ string uifile = "gnome-disk-health.ui";
+
+ public bool create_widgets(string disk_string) {
+ try {
+ add_from_file (uifile);
+ Gtk.Widget window = (Gtk.Widget) get_object("DiskHealthDialog");
+
+ if (!fill_in_data(disk_string))
+ return false;
+
+ window.show_all();
+ window.destroy += Gtk.main_quit;
+ } catch (GLib.Error e) {
+ stderr.printf("Failed to create main window: %s\n", e.message);
+ return false;
+ }
+
+ return true;
+ }
+
+ private DBus.Connection connection;
+ private dynamic DBus.Object manager;
+ private DBus.ObjectPath dbus_path;
+ private dynamic DBus.Object disk;
+
+ public bool fill_in_data(string disk_string) {
+
+ try {
+ connection = DBus.Bus.get(DBus.BusType.SYSTEM);
+
+ manager = connection.get_object("net.poettering.SmartKit", "/", "net.poettering.SmartKit.Manager");
+
+ DBus.ObjectPath p;
+
+ try {
+ p = manager.getDiskByPath(disk_string);
+ } catch (DBus.Error e) {
+ try {
+ p = manager.getDiskByUDI(disk_string);
+ } catch (DBus.Error e) {
+ return false;
+ }
+ }
+
+ stderr.printf("Using D-Bus path %s\n", p);
+
+ disk = connection.get_object("net.poettering.SmartKit", p, "net.poettering.SmartKit.Disk");
+
+ ((Gtk.Label) get_object("pathLabel")).set_label(disk.getPath());
+ ((Gtk.Label) get_object("sizeLabel")).set_label(pretty_size(disk.getSize()));
+
+ bool b = disk.isIdentifyAvailable();
+
+ if (b) {
+ ((Gtk.Label) get_object("modelLabel")).set_label(disk.getIdentifyModel());
+ ((Gtk.Label) get_object("serialLabel")).set_label(disk.getIdentifySerial());
+ ((Gtk.Label) get_object("firmwareLabel")).set_label(disk.getIdentifyFirmware());
+ } else {
+ ((Gtk.Label) get_object("modelLabel")).set_label("n/a");
+ ((Gtk.Label) get_object("serialLabel")).set_label("n/a");
+ ((Gtk.Label) get_object("firmwareLabel")).set_label("n/a");
+ }
+
+ if (b)
+ b = disk.isSmartAvailable();
+
+ if (b) {
+ ((Gtk.Label) get_object("smartLabel")).set_label("Disk health functionality (S.M.A.R.T.) is available.");
+ } else {
+ ((Gtk.Label) get_object("smartLabel")).set_markup("Disk health functionality (S.M.A.R.T.) is <b>not</b> available.");
+ ((Gtk.Label) get_object("healthLabel")).set_label("");
+ ((Gtk.Label) get_object("badSectorsLabel")).set_label("");
+ ((Gtk.Label) get_object("temperatureLabel")).set_label("");
+ }
+
+ } catch (DBus.Error e) {
+ stderr.printf("D-Bus error: %s\n", e.message);
+ return false;
+ }
+
+ return true;
+ }
+
+ public static string pretty_size(uint64 size) {
+
+ if (size >= (uint64)1024*(uint64)1024*(uint64)1024*(uint64)1024)
+ return "%0.1f TiB".printf((double) size/1024/1024/1024/1024);
+ else if (size >= (uint64)1024*(uint64)1024*(uint64)1024)
+ return "%0.1f GiB".printf((double) size/1024/1024/1024);
+ else if (size >= (uint64)1024*(uint64)1024)
+ return "%0.1f MiB".printf((double) size/1024/1024);
+ else if (size >= (uint64)1024)
+ return "%0.1f KiB".printf((double) size/1024);
+ else
+ return "%u B".printf((uint) size);
+ }
+
+
+ public static int main (string[] args) {
+ Gtk.init(ref args);
+
+ var dh = new DiskHealth();
+
+ if (dh.create_widgets(args[1]))
+ Gtk.main ();
+
+ return 0;
+ }
+}
diff --git a/skdump.c b/skdump.c
index 6fa9167..7d1cdb6 100644
--- a/skdump.c
+++ b/skdump.c
@@ -1,23 +1,53 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ This file is part of SmartKit.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#include <string.h>
#include <errno.h>
#include "smart.h"
int main(int argc, char *argv[]) {
- int ret;
- const char *device;
- SkDevice *d;
+ int ret;
+ const char *device;
+ SkDisk *d;
+
+ if (argc != 2) {
+ g_printerr("%s [DEVICE]", argv[0]);
+ return 1;
+ }
- device = argc >= 2 ? argv[1] : "/dev/sda";
+ device = argv[1];
- if ((ret = sk_disk_open(device, &d)) < 0) {
- g_printerr("Failed to open disk %s: %s\n", device, strerror(errno));
- return 1;
- }
+ if ((ret = sk_disk_open(device, &d)) < 0) {
+ g_printerr("Failed to open disk %s: %s\n", device, g_strerror(errno));
+ return 1;
+ }
- sk_disk_dump(d);
+ if ((ret = sk_disk_dump(d)) < 0) {
+ g_printerr("Failed to dump disk data: %s\n", g_strerror(errno));
+ return 1;
+ }
- sk_disk_free(d);
+ sk_disk_free(d);
- return 0;
+ return 0;
}
diff --git a/sktest.c b/sktest.c
new file mode 100644
index 0000000..374b11c
--- /dev/null
+++ b/sktest.c
@@ -0,0 +1,66 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ This file is part of SmartKit.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <errno.h>
+
+#include "smart.h"
+
+int main(int argc, char *argv[]) {
+ int ret;
+ const char *device;
+ SkDisk *d;
+ SkSmartSelfTest test;
+
+ if (argc < 3) {
+ g_printerr("%s [DEVICE] [short|extended|conveyance]\n", argv[0]);
+ return 1;
+ }
+
+ device = argv[1];
+
+ if (!g_strcasecmp(argv[2], sk_smart_self_test_to_string(SK_SMART_SELF_TEST_SHORT)))
+ test = SK_SMART_SELF_TEST_SHORT;
+ else if (!g_strcasecmp(argv[2], sk_smart_self_test_to_string(SK_SMART_SELF_TEST_EXTENDED)))
+ test = SK_SMART_SELF_TEST_EXTENDED;
+ else if (!(g_strcasecmp(argv[2], sk_smart_self_test_to_string(SK_SMART_SELF_TEST_CONVEYANCE))))
+ test = SK_SMART_SELF_TEST_CONVEYANCE;
+ else {
+ g_printerr("Unknown test '%s'.\n", argv[2]);
+ return 1;
+ }
+
+ if ((ret = sk_disk_open(device, &d)) < 0) {
+ g_printerr("Failed to open disk %s: %s\n", device, g_strerror(errno));
+ return 1;
+ }
+
+ if ((ret = sk_disk_smart_self_test(d, test)) < 0) {
+ g_printerr("Failed to start sel-test: %s\n", g_strerror(errno));
+ return 1;
+
+ }
+
+ sk_disk_free(d);
+
+ return 0;
+}
diff --git a/smart.c b/smart.c
index 7d4c483..4cd9c6f 100644
--- a/smart.c
+++ b/smart.c
@@ -1,3 +1,25 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ This file is part of SmartKit.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -6,6 +28,8 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
#include <sys/ioctl.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
@@ -19,390 +43,397 @@
#define SK_TIMEOUT 2000
typedef enum SkDirection {
- SK_DIRECTION_NONE,
- SK_DIRECTION_IN,
- SK_DIRECTION_OUT,
- _SK_DIRECTION_MAX
+ SK_DIRECTION_NONE,
+ SK_DIRECTION_IN,
+ SK_DIRECTION_OUT,
+ _SK_DIRECTION_MAX
} SkDirection;
typedef enum SkDiskType {
- SK_DISK_TYPE_ATA_PASSTHROUGH, /* ATA passthrough over SCSI transport */
- SK_DISK_TYPE_ATA,
- SK_DISK_TYPE_UNKNOWN,
- _SK_DISK_TYPE_MAX
+ SK_DISK_TYPE_ATA_PASSTHROUGH, /* ATA passthrough over SCSI transport */
+ SK_DISK_TYPE_ATA,
+ SK_DISK_TYPE_UNKNOWN,
+ _SK_DISK_TYPE_MAX
} SkDiskType;
struct SkDisk {
- gchar *name;
- int fd;
- SkDiskType type;
+ gchar *name;
+ int fd;
+ SkDiskType type;
- guint64 size;
+ guint64 size;
- guint8 identify[512];
- guint8 smart_data[512];
- guint8 smart_threshold_data[512];
+ guint8 identify[512];
+ guint8 smart_data[512];
+ guint8 smart_threshold_data[512];
- gboolean identify_data_valid:1;
- gboolean smart_data_valid:1;
- gboolean smart_threshold_data_valid:1;
+ gboolean identify_data_valid:1;
+ gboolean smart_data_valid:1;
+ gboolean smart_threshold_data_valid:1;
- SkIdentifyParsedData identify_parsed_data;
- SkSmartParsedData smart_parsed_data;
+ SkIdentifyParsedData identify_parsed_data;
+ SkSmartParsedData smart_parsed_data;
};
/* ATA commands */
typedef enum SkAtaCommand {
- SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
- SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
- SK_ATA_COMMAND_SMART = 0xB0,
- SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
+ SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC,
+ SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1,
+ SK_ATA_COMMAND_SMART = 0xB0,
+ SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5
} SkAtaCommand;
/* ATA SMART subcommands (ATA8 7.52.1) */
typedef enum SkSmartCommand {
- SK_SMART_COMMAND_READ_DATA = 0xD0,
- SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
- SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
- SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
- SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
- SK_SMART_COMMAND_RETURN_STATUS = 0xDA
+ SK_SMART_COMMAND_READ_DATA = 0xD0,
+ SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1,
+ SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4,
+ SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8,
+ SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9,
+ SK_SMART_COMMAND_RETURN_STATUS = 0xDA
} SkSmartCommand;
-/* ATA SMART test type (ATA8 7.52.5.2) */
-typedef enum SkSmartTest {
- SK_SMART_TEST_OFFLINE_FULL = 0,
- SK_SMART_TEST_OFFLINE_SHORT = 1,
- SK_SMART_TEST_OFFLINE_EXTENDED = 2,
- SK_SMART_TEST_OFFLINE_CONVEYANCE = 3,
- SK_SMART_TEST_OFFLINE_SELECTIVE = 4,
+static gboolean disk_smart_is_available(SkDisk *d) {
+ return d->identify_data_valid && !!(d->identify[164] & 1);
+}
+
+static gboolean disk_smart_is_enabled(SkDisk *d) {
+ return d->identify_data_valid && !!(d->identify[170] & 1);
+}
- SK_SMART_TEST_CAPTIVE_SHORT = 129,
- SK_SMART_TEST_CAPTIVE_EXTENDED = 130,
- SK_SMART_TEST_CAPTIVE_CONVEYANCE = 131,
- SK_SMART_TEST_CAPTIVE_SELECTIVE = 132,
+static gboolean disk_smart_is_conveyance_test_available(SkDisk *d) {
+ g_assert(d->smart_data_valid);
- SK_SMART_TEST_CAPTIVE_MASK = 128,
- SK_SMART_TEST_ABORT = 127
-} SkSmartTest;
+ return !!(d->smart_data[367] & 32);
+}
+static gboolean disk_smart_is_short_and_extended_test_available(SkDisk *d) {
+ g_assert(d->smart_data_valid);
-static gboolean disk_smart_is_available(SkDisk *d) {
- return d->identify_data_valid && !!(d->identify[164] & 1);
+ return !!(d->smart_data[367] & 16);
}
-static gboolean disk_smart_is_enabled(SkDisk *d) {
- return d->identify_data_valid && !!(d->identify[170] & 1);
+static gboolean disk_smart_is_start_test_available(SkDisk *d) {
+ g_assert(d->smart_data_valid);
+
+ return !!(d->smart_data[367] & 1);
+}
+
+static gboolean disk_smart_is_abort_test_available(SkDisk *d) {
+ g_assert(d->smart_data_valid);
+
+ return !!(d->smart_data[367] & 41);
}
static int disk_ata_command(SkDisk *d, SkAtaCommand command, SkDirection direction, gpointer cmd_data, gpointer data, size_t *len) {
- guint8 *bytes = cmd_data;
- int ret;
+ guint8 *bytes = cmd_data;
+ int ret;
- g_assert(d->type == SK_DISK_TYPE_ATA);
+ g_assert(d->type == SK_DISK_TYPE_ATA);
- switch (direction) {
+ switch (direction) {
- case SK_DIRECTION_OUT:
+ case SK_DIRECTION_OUT:
- /* We could use HDIO_DRIVE_TASKFILE here, but that's a
- * deprecated ioctl(), hence we don't do it. */
+ /* We could use HDIO_DRIVE_TASKFILE here, but
+ * that's a deprecated ioctl(), hence we don't
+ * do it. And we don't need writing anyway. */
- errno = ENOTSUP;
- return -1;
+ errno = ENOTSUP;
+ return -1;
- case SK_DIRECTION_IN: {
- guint8 *ioctl_data;
+ case SK_DIRECTION_IN: {
+ guint8 *ioctl_data;
- /* We have HDIO_DRIVE_CMD which can only read, but not write,
- * and cannot do LBA. We use it for all read commands. */
+ /* We have HDIO_DRIVE_CMD which can only read, but not write,
+ * and cannot do LBA. We use it for all read commands. */
- ioctl_data = g_alloca(4 + *len);
- memset(ioctl_data, 0, 4 + *len);
+ ioctl_data = g_alloca(4 + *len);
+ memset(ioctl_data, 0, 4 + *len);
- ioctl_data[0] = (guint8) command; /* COMMAND */
- ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
- ioctl_data[2] = bytes[1]; /* FEATURE */
- ioctl_data[3] = bytes[3]; /* NSECTOR */
+ ioctl_data[0] = (guint8) command; /* COMMAND */
+ ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */
+ ioctl_data[2] = bytes[1]; /* FEATURE */
+ ioctl_data[3] = bytes[3]; /* NSECTOR */
- if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
- return ret;
+ if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0)
+ return ret;
- memset(bytes, 0, 12);
- bytes[11] = ioctl_data[0];
- bytes[1] = ioctl_data[1];
- bytes[3] = ioctl_data[2];
+ memset(bytes, 0, 12);
+ bytes[11] = ioctl_data[0];
+ bytes[1] = ioctl_data[1];
+ bytes[3] = ioctl_data[2];
- memcpy(data, ioctl_data+4, *len);
+ memcpy(data, ioctl_data+4, *len);
- return ret;
- }
+ return ret;
+ }
- case SK_DIRECTION_NONE: {
- guint8 ioctl_data[7];
+ case SK_DIRECTION_NONE: {
+ guint8 ioctl_data[7];
- /* We have HDIO_DRIVE_TASK which can neither read nor
- * write, but can do LBA. We use it for all commands that
- * do neither read nor write */
+ /* We have HDIO_DRIVE_TASK which can neither read nor
+ * write, but can do LBA. We use it for all commands that
+ * do neither read nor write */
- memset(ioctl_data, 0, sizeof(ioctl_data));
+ memset(ioctl_data, 0, sizeof(ioctl_data));
- ioctl_data[0] = (guint8) command; /* COMMAND */
- ioctl_data[1] = bytes[1]; /* FEATURE */
- ioctl_data[2] = bytes[3]; /* NSECTOR */
+ ioctl_data[0] = (guint8) command; /* COMMAND */
+ ioctl_data[1] = bytes[1]; /* FEATURE */
+ ioctl_data[2] = bytes[3]; /* NSECTOR */
- ioctl_data[3] = bytes[9]; /* LBA LOW */
- ioctl_data[4] = bytes[8]; /* LBA MID */
- ioctl_data[5] = bytes[7]; /* LBA HIGH */
- ioctl_data[6] = bytes[10]; /* SELECT */
+ ioctl_data[3] = bytes[9]; /* LBA LOW */
+ ioctl_data[4] = bytes[8]; /* LBA MID */
+ ioctl_data[5] = bytes[7]; /* LBA HIGH */
+ ioctl_data[6] = bytes[10]; /* SELECT */
- if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
- return ret;
+ if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data)))
+ return ret;
- memset(bytes, 0, 12);
- bytes[11] = ioctl_data[0];
- bytes[1] = ioctl_data[1];
- bytes[3] = ioctl_data[2];
+ memset(bytes, 0, 12);
+ bytes[11] = ioctl_data[0];
+ bytes[1] = ioctl_data[1];
+ bytes[3] = ioctl_data[2];
- bytes[9] = ioctl_data[3];
- bytes[8] = ioctl_data[4];
- bytes[7] = ioctl_data[5];
+ bytes[9] = ioctl_data[3];
+ bytes[8] = ioctl_data[4];
+ bytes[7] = ioctl_data[5];
- bytes[10] = ioctl_data[6];
+ bytes[10] = ioctl_data[6];
- return ret;
- }
+ return ret;
+ }
- default:
- g_assert_not_reached();
- return -1;
- }
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
}
/* Sends a SCSI command block */
static int sg_io(int fd, int direction,
- const void *cdb, size_t cdb_len,
- void *data, size_t data_len,
- void *sense, size_t sense_len) {
+ const void *cdb, size_t cdb_len,
+ void *data, size_t data_len,
+ void *sense, size_t sense_len) {
- struct sg_io_hdr io_hdr;
+ struct sg_io_hdr io_hdr;
- memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+ memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
- io_hdr.interface_id = 'S';
- io_hdr.cmdp = (unsigned char*) cdb;
- io_hdr.cmd_len = cdb_len;
- io_hdr.dxferp = data;
- io_hdr.dxfer_len = data_len;
- io_hdr.sbp = sense;
- io_hdr.mx_sb_len = sense_len;
- io_hdr.dxfer_direction = direction;
- io_hdr.timeout = SK_TIMEOUT;
+ io_hdr.interface_id = 'S';
+ io_hdr.cmdp = (unsigned char*) cdb;
+ io_hdr.cmd_len = cdb_len;
+ io_hdr.dxferp = data;
+ io_hdr.dxfer_len = data_len;
+ io_hdr.sbp = sense;
+ io_hdr.mx_sb_len = sense_len;
+ io_hdr.dxfer_direction = direction;
+ io_hdr.timeout = SK_TIMEOUT;
- return ioctl(fd, SG_IO, &io_hdr);
+ return ioctl(fd, SG_IO, &io_hdr);
}
static int disk_passthrough_command(SkDisk *d, SkAtaCommand command, SkDirection direction, gpointer cmd_data, gpointer data, size_t *len) {
- guint8 *bytes = cmd_data;
- guint8 cdb[16];
- guint8 sense[32];
- guint8 *desc = sense+8;
- int ret;
+ guint8 *bytes = cmd_data;
+ guint8 cdb[16];
+ guint8 sense[32];
+ guint8 *desc = sense+8;
+ int ret;
- static const int direction_map[] = {
- [SK_DIRECTION_NONE] = SG_DXFER_NONE,
- [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
- [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
- };
+ static const int direction_map[] = {
+ [SK_DIRECTION_NONE] = SG_DXFER_NONE,
+ [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV,
+ [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV
+ };
- g_assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH);
+ g_assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH);
- /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
- * ATA Command Pass-Through":
- * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
+ /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8
+ * ATA Command Pass-Through":
+ * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */
- memset(cdb, 0, sizeof(cdb));
+ memset(cdb, 0, sizeof(cdb));
- cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
+ cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */
- if (direction == SK_DIRECTION_NONE) {
- cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
- cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
+ if (direction == SK_DIRECTION_NONE) {
+ cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */
+ cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */
- } else if (direction == SK_DIRECTION_IN) {
- cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
- cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
+ } else if (direction == SK_DIRECTION_IN) {
+ cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */
+ cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */
- } else if (direction == SK_DIRECTION_OUT) {
- cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
- cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
- }
+ } else if (direction == SK_DIRECTION_OUT) {
+ cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */
+ cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */
+ }
- cdb[3] = bytes[0]; /* FEATURES */
- cdb[4] = bytes[1];
+ cdb[3] = bytes[0]; /* FEATURES */
+ cdb[4] = bytes[1];
- cdb[5] = bytes[2]; /* SECTORS */
- cdb[6] = bytes[3];
+ cdb[5] = bytes[2]; /* SECTORS */
+ cdb[6] = bytes[3];
- cdb[8] = bytes[9]; /* LBA LOW */
- cdb[10] = bytes[8]; /* LBA MED */
- cdb[12] = bytes[7]; /* LBA HIGH */
+ cdb[8] = bytes[9]; /* LBA LOW */
+ cdb[10] = bytes[8]; /* LBA MED */
+ cdb[12] = bytes[7]; /* LBA HIGH */
- cdb[13] = bytes[10] & 0x4F; /* SELECT */
- cdb[14] = (guint8) command;
+ cdb[13] = bytes[10] & 0x4F; /* SELECT */
+ cdb[14] = (guint8) command;
- if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[6] * 512, sense, sizeof(sense))) < 0)
- return ret;
+ if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[6] * 512, sense, sizeof(sense))) < 0)
+ return ret;
- if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
- errno = EIO;
- return -1;
- }
+ if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) {
+ errno = EIO;
+ return -1;
+ }
- memset(bytes, 0, 12);
+ memset(bytes, 0, 12);
- bytes[1] = desc[3];
- bytes[2] = desc[4];
- bytes[3] = desc[5];
- bytes[9] = desc[7];
- bytes[8] = desc[9];
- bytes[7] = desc[10];
- bytes[10] = desc[12];
- bytes[11] = desc[13];
+ bytes[1] = desc[3];
+ bytes[2] = desc[4];
+ bytes[3] = desc[5];
+ bytes[9] = desc[7];
+ bytes[8] = desc[9];
+ bytes[7] = desc[10];
+ bytes[10] = desc[12];
+ bytes[11] = desc[13];
- return ret;
+ return ret;
}
static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, gpointer cmd_data, gpointer data, size_t *len) {
- static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, gpointer cmd_data, gpointer data, size_t *len) = {
- [SK_DISK_TYPE_ATA] = disk_ata_command,
- [SK_DISK_TYPE_ATA_PASSTHROUGH] = disk_passthrough_command,
- };
+ static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, gpointer cmd_data, gpointer data, size_t *len) = {
+ [SK_DISK_TYPE_ATA] = disk_ata_command,
+ [SK_DISK_TYPE_ATA_PASSTHROUGH] = disk_passthrough_command,
+ };
- g_assert(d);
- g_assert(d->type <= _SK_DISK_TYPE_MAX);
- g_assert(direction <= _SK_DIRECTION_MAX);
+ g_assert(d);
+ g_assert(d->type <= _SK_DISK_TYPE_MAX);
+ g_assert(direction <= _SK_DIRECTION_MAX);
- g_assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
- g_assert(direction != SK_DIRECTION_NONE || (!data && !len));
+ g_assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0));
+ g_assert(direction != SK_DIRECTION_NONE || (!data && !len));
- return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
+ return disk_command_table[d->type](d, command, direction, cmd_data, data, len);
}
static int disk_identify_device(SkDisk *d) {
- guint16 cmd[6];
- int ret;
- size_t len = 512;
+ guint16 cmd[6];
+ int ret;
+ size_t len = 512;
- memset(cmd, 0, sizeof(cmd));
+ memset(cmd, 0, sizeof(cmd));
- cmd[1] = GUINT16_TO_BE(1);
+ cmd[1] = GUINT16_TO_BE(1);
- if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
- return ret;
+ if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0)
+ return ret;
- if (len != 512) {
- errno = EIO;
- return -1;
- }
+ if (len != 512) {
+ errno = EIO;
+ return -1;
+ }
- d->identify_data_valid = TRUE;
+ d->identify_data_valid = TRUE;
- return 0;
+ return 0;
}
-int sk_disk_check_power_mode(SkDisk *d, gboolean *mode) {
- int ret;
- guint16 cmd[6];
+int sk_disk_check_sleep_mode(SkDisk *d, gboolean *awake) {
+ int ret;
+ guint16 cmd[6];
- if (!d->identify_data_valid) {
- errno = ENOTSUP;
- return -1;
- }
+ if (!d->identify_data_valid) {
+ errno = ENOTSUP;
+ return -1;
+ }
- memset(cmd, 0, sizeof(cmd));
+ memset(cmd, 0, sizeof(cmd));
- if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
- return ret;
+ if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0)
+ return ret;
- if (cmd[0] != 0 || (GUINT16_FROM_BE(cmd[5]) & 1) != 0) {
- errno = EIO;
- return -1;
- }
+ if (cmd[0] != 0 || (GUINT16_FROM_BE(cmd[5]) & 1) != 0) {
+ errno = EIO;
+ return -1;
+ }
- *mode = GUINT16_FROM_BE(cmd[1]) == 0xFF;
+ *awake = GUINT16_FROM_BE(cmd[1]) == 0xFF;
- return 0;
+ return 0;
}
static int disk_smart_enable(SkDisk *d, gboolean b) {
- guint16 cmd[6];
+ guint16 cmd[6];
- if (!disk_smart_is_available(d)) {
- errno = ENOTSUP;
- return -1;
- }
+ if (!disk_smart_is_available(d)) {
+ errno = ENOTSUP;
+ return -1;
+ }
- memset(cmd, 0, sizeof(cmd));
+ memset(cmd, 0, sizeof(cmd));
- cmd[0] = GUINT16_TO_BE(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
- cmd[2] = GUINT16_TO_BE(0x0000U);
- cmd[3] = GUINT16_TO_BE(0x00C2U);
- cmd[4] = GUINT16_TO_BE(0x4F00U);
+ cmd[0] = GUINT16_TO_BE(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS);
+ cmd[2] = GUINT16_TO_BE(0x0000U);
+ cmd[3] = GUINT16_TO_BE(0x00C2U);
+ cmd[4] = GUINT16_TO_BE(0x4F00U);
- return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
+ return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0);
}
int sk_disk_smart_read_data(SkDisk *d) {
- guint16 cmd[6];
- int ret;
- size_t len = 512;
+ guint16 cmd[6];
+ int ret;
+ size_t len = 512;
- if (!disk_smart_is_available(d)) {
- errno = ENOTSUP;
- return -1;
- }
+ if (!disk_smart_is_available(d)) {
+ errno = ENOTSUP;
+ return -1;
+ }
- memset(cmd, 0, sizeof(cmd));
+ memset(cmd, 0, sizeof(cmd));
- cmd[0] = GUINT16_TO_BE(SK_SMART_COMMAND_READ_DATA);
- cmd[1] = GUINT16_TO_BE(1);
- cmd[2] = GUINT16_TO_BE(0x0000U);
- cmd[3] = GUINT16_TO_BE(0x00C2U);
- cmd[4] = GUINT16_TO_BE(0x4F00U);
+ cmd[0] = GUINT16_TO_BE(SK_SMART_COMMAND_READ_DATA);
+ cmd[1] = GUINT16_TO_BE(1);
+ cmd[2] = GUINT16_TO_BE(0x0000U);
+ cmd[3] = GUINT16_TO_BE(0x00C2U);
+ cmd[4] = GUINT16_TO_BE(0x4F00U);
- if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
- return ret;
+ if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0)
+ return ret;
- d->smart_data_valid = TRUE;
+ d->smart_data_valid = TRUE;
- return ret;
+ return ret;
}
static int disk_smart_read_thresholds(SkDisk *d) {
- guint16 cmd[6];
- int ret;
- size_t len = 512;
+ guint16 cmd[6];
+ int ret;
+ size_t len = 512;
- if (!disk_smart_is_available(d)) {
- errno = ENOTSUP;
- return -1;
- }
+ if (!disk_smart_is_available(d)) {
+ errno = ENOTSUP;
+ return -1;
+ }
- memset(cmd, 0, sizeof(cmd));
+ memset(cmd, 0, sizeof(cmd));
- cmd[0] = GUINT16_TO_BE(SK_SMART_COMMAND_READ_THRESHOLDS);
- cmd[1] = GUINT16_TO_BE(1);
- cmd[2] = GUINT16_TO_BE(0x0000U);
- cmd[3] = GUINT16_TO_BE(0x00C2U);
- cmd[4] = GUINT16_TO_BE(0x4F00U);
+ cmd[0] = GUINT16_TO_BE(SK_SMART_COMMAND_READ_THRESHOLDS);
+ cmd[1] = GUINT16_TO_BE(1);
+ cmd[2] = GUINT16_TO_BE(0x0000U);
+ cmd[3] = GUINT16_TO_BE(0x00C2U);
+ cmd[4] = GUINT16_TO_BE(0x4F00U);
- if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_threshold_data, &len)) < 0)
- return ret;
+ if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_threshold_data, &len)) < 0)
+ return ret;
- d->smart_threshold_data_valid = TRUE;
+ d->smart_threshold_data_valid = TRUE;
- return ret;
+ return ret;
}
/* int disk_smart_status(SkDisk *d, SmartLogAddress a, gboolean *b) { */
@@ -415,626 +446,766 @@ static int disk_smart_read_thresholds(SkDisk *d) {
/* cmd[3] = GUINT16_TO_BE(0x00C2U); */
/* cmd[4] = GUINT16_TO_BE(0x4F00U | (guint16) a); */
-/* ret = disk_ata_command(SK_ATA_SMART, cmd, sizeof(cmd), NULL, 0); */
+/* ret = disk_command(SK_ATA_SMART, cmd, sizeof(cmd), NULL, 0); */
/* return ret; */
/* } */
-/* int disk_smart_immediate_offline(SkDisk *d, SmartTestType type) { */
-/* guint16 cmd[6]; */
+int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) {
+ guint16 cmd[6];
+ int ret;
-/* memset(cmd, 0, sizeof(cmd)); */
+ if (!disk_smart_is_available(d)) {
+ errno = ENOTSUP;
+ return -1;
+ }
-/* cmd[0] = GUINT16_TO_BE(SMART_EXECUTE_OFFLINE_IMMEDIATE); */
-/* cmd[2] = GUINT16_TO_BE(0x0000U); */
-/* cmd[3] = GUINT16_TO_BE(0x00C2U); */
-/* cmd[4] = GUINT16_TO_BE(0x4F00U | (guint16) type); */
+ if (!d->smart_data_valid)
+ if ((ret = sk_disk_smart_read_data(d)) < 0)
+ return -1;
-/* return disk_ata_command(SK_ATA_SMART, cmd, sizeof(cmd), NULL, 0); */
-/* } */
+ g_assert(d->smart_data_valid);
+
+ if (test != SK_SMART_SELF_TEST_SHORT &&
+ test != SK_SMART_SELF_TEST_EXTENDED &&
+ test != SK_SMART_SELF_TEST_CONVEYANCE &&
+ test != SK_SMART_SELF_TEST_ABORT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!disk_smart_is_start_test_available(d)
+ || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d))
+ || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d))
+ || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ if (test == SK_SMART_SELF_TEST_ABORT &&
+ !disk_smart_is_abort_test_available(d)) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ memset(cmd, 0, sizeof(cmd));
+
+ cmd[0] = GUINT16_TO_BE(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE);
+ cmd[2] = GUINT16_TO_BE(0x0000U);
+ cmd[3] = GUINT16_TO_BE(0x00C2U);
+ cmd[4] = GUINT16_TO_BE(0x4F00U | (guint16) test);
+
+ return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL);
+}
static void swap_strings(gchar *s, size_t len) {
- g_assert((len & 1) == 0);
-
- for (; len > 0; s += 2, len -= 2) {
- gchar t;
- t = s[0];
- s[0] = s[1];
- s[1] = t;
- }
+ g_assert((len & 1) == 0);
+
+ for (; len > 0; s += 2, len -= 2) {
+ gchar t;
+ t = s[0];
+ s[0] = s[1];
+ s[1] = t;
+ }
}
static void clean_strings(gchar *s) {
- gchar *e;
+ gchar *e;
- for (e = s; *e; e++)
- if (!g_ascii_isprint(*e))
- *e = ' ';
+ for (e = s; *e; e++)
+ if (!g_ascii_isprint(*e))
+ *e = ' ';
}
static void drop_spaces(gchar *s) {
- gchar *d = s;
- gboolean prev_space = FALSE;
-
- s += strspn(s, " ");
-
- for (;*s; s++) {
-
- if (prev_space) {
- if (*s != ' ') {
- prev_space = FALSE;
- *(d++) = ' ';
- }
- } else {
- if (*s == ' ')
- prev_space = TRUE;
- else
- *(d++) = *s;
+ gchar *d = s;
+ gboolean prev_space = FALSE;
+
+ s += strspn(s, " ");
+
+ for (;*s; s++) {
+
+ if (prev_space) {
+ if (*s != ' ') {
+ prev_space = FALSE;
+ *(d++) = ' ';
+ }
+ } else {
+ if (*s == ' ')
+ prev_space = TRUE;
+ else
+ *(d++) = *s;
+ }
}
- }
- *d = 0;
+ *d = 0;
}
static void read_string(gchar *d, guint8 *s, size_t len) {
- memcpy(d, s, len);
- d[len] = 0;
- swap_strings(d, len);
- clean_strings(d);
- drop_spaces(d);
+ memcpy(d, s, len);
+ d[len] = 0;
+ swap_strings(d, len);
+ clean_strings(d);
+ drop_spaces(d);
}
int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) {
- if (!d->identify_data_valid) {
- errno = ENOENT;
- return -1;
- }
+ if (!d->identify_data_valid) {
+ errno = ENOENT;
+ return -1;
+ }
- read_string(d->identify_parsed_data.serial, d->identify+20, 20);
- read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
- read_string(d->identify_parsed_data.model, d->identify+54, 40);
+ read_string(d->identify_parsed_data.serial, d->identify+20, 20);
+ read_string(d->identify_parsed_data.firmware, d->identify+46, 8);
+ read_string(d->identify_parsed_data.model, d->identify+54, 40);
- *ipd = &d->identify_parsed_data;
+ *ipd = &d->identify_parsed_data;
- return 0;
+ return 0;
}
int sk_disk_smart_is_available(SkDisk *d, gboolean *b) {
- if (!d->identify_data_valid) {
- errno = ENOTSUP;
- return -1;
- }
+ if (!d->identify_data_valid) {
+ errno = ENOTSUP;
+ return -1;
+ }
- *b = disk_smart_is_available(d);
- return 0;
+ *b = disk_smart_is_available(d);
+ return 0;
}
int sk_disk_identify_is_available(SkDisk *d, gboolean *b) {
- *b = d->identify_data_valid;
- return 0;
+ *b = d->identify_data_valid;
+ return 0;
}
const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) {
- static const char* const map[] = {
- [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
- [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
- [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
- [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
- [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
- [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
- [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
- };
-
- if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
+ static const char* const map[] = {
+ [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.",
+ [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.",
+ [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.",
+ [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.",
+ [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.",
+ [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.",
+ [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status"
+ };
+
+ if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX)
+ return NULL;
+
+ return map[status];
+}
+
+const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) {
+
+ static const char* const map[] = {
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER] = "The previous self-test routine completed without error or no self-test has ever been run.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL] = "A fatal error or unknown test error occurred while the device was executing its self-test routine and the device was unable to complete the self-test routine.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN] = "The previous self-test completed having a test element that failed and the test element that failed.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO] = "The previous self-test completed having the servo (and/or seek) test element of the test failed.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING] = "The previous self-test completed having a test element that failed and the device is suspected of having handling damage.",
+ [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress"
+ };
+
+ if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX)
+ return NULL;
+
+ return map[status];
+}
+
+const char* sk_smart_self_test_to_string(SkSmartSelfTest test) {
+
+ switch (test) {
+ case SK_SMART_SELF_TEST_SHORT:
+ return "short";
+ case SK_SMART_SELF_TEST_EXTENDED:
+ return "extended";
+ case SK_SMART_SELF_TEST_CONVEYANCE:
+ return "conveyance";
+ case SK_SMART_SELF_TEST_ABORT:
+ return "abort";
+ }
+
return NULL;
+}
- return map[status];
+gboolean sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) {
+
+ if (!d->start_test_available)
+ return FALSE;
+
+ switch (test) {
+ case SK_SMART_SELF_TEST_SHORT:
+ case SK_SMART_SELF_TEST_EXTENDED:
+ return d->short_and_extended_test_available;
+ case SK_SMART_SELF_TEST_CONVEYANCE:
+ return d->conveyance_test_available;
+ case SK_SMART_SELF_TEST_ABORT:
+ return d->abort_test_available;
+ default:
+ return FALSE;
+ }
+}
+
+unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) {
+
+ if (!sk_smart_self_test_available(d, test))
+ return 0;
+
+ switch (test) {
+ case SK_SMART_SELF_TEST_SHORT:
+ return d->short_test_polling_minutes;
+ case SK_SMART_SELF_TEST_EXTENDED:
+ return d->extended_test_polling_minutes;
+ case SK_SMART_SELF_TEST_CONVEYANCE:
+ return d->conveyance_test_polling_minutes;
+ default:
+ return 0;
+ }
}
typedef struct SkSmartAttributeInfo {
- const char *name;
- SkSmartAttributeUnit unit;
+ const char *name;
+ SkSmartAttributeUnit unit;
} SkSmartAttributeInfo;
/* This data is stolen from smartmontools */
static const SkSmartAttributeInfo const attribute_info[255] = {
- [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [2] = { "throughput-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
- [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [8] = { "seek-time-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
- [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_KELVIN },
- [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_KELVIN },
- [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
- [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
- [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [204] = { "shock-count-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [205] = { "shock-rate-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
- [209] = { "offline-seek-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
- [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [225] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
- [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [228] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
- [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
- [231] = { "temperature-celsius-1", SK_SMART_ATTRIBUTE_UNIT_KELVIN },
- [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
- [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [2] = { "throughput-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
+ [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [8] = { "seek-time-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
+ [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_KELVIN },
+ [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_KELVIN },
+ [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
+ [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS },
+ [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [204] = { "shock-count-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [205] = { "shock-rate-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN},
+ [209] = { "offline-seek-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
+ [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [225] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
+ [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [228] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE },
+ [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN },
+ [231] = { "temperature-celsius-1", SK_SMART_ATTRIBUTE_UNIT_KELVIN },
+ [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS },
+ [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }
};
-static void make_pretty(SkSmartAttribute *a) {
- guint64 fourtyeight;
-
- if (!a->name)
- return;
-
- if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
- return;
-
- fourtyeight =
- ((guint64) a->raw[0]) |
- (((guint64) a->raw[1]) << 8) |
- (((guint64) a->raw[2]) << 16) |
- (((guint64) a->raw[3]) << 24) |
- (((guint64) a->raw[4]) << 32) |
- (((guint64) a->raw[5]) << 40);
-
- if (!strcmp(a->name, "spin-up-time"))
- a->pretty_value = fourtyeight & 0xFFFF;
- else if (!strcmp(a->name, "airflow-temperature-celsius") ||
- !strcmp(a->name, "temperature-celsius-1") ||
- !strcmp(a->name, "temperature-celsius-2")) {
- a->pretty_value = (fourtyeight & 0xFFFF) + 273;
- } else if (!strcmp(a->name, "power-on-minutes"))
- a->pretty_value = fourtyeight * 60 * 1000;
- else if (!strcmp(a->name, "power-on-seconds"))
- a->pretty_value = fourtyeight * 1000;
- else if (!strcmp(a->name, "power-on-hours") ||
- !strcmp(a->name, "loaded-hours") ||
- !strcmp(a->name, "head-flying-hours"))
- a->pretty_value = fourtyeight * 60 * 60 * 1000;
- else
- a->pretty_value = fourtyeight;
+static void make_pretty(SkSmartAttributeParsedData *a) {
+ guint64 fourtyeight;
+
+ if (!a->name)
+ return;
+
+ if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN)
+ return;
+
+ fourtyeight =
+ ((guint64) a->raw[0]) |
+ (((guint64) a->raw[1]) << 8) |
+ (((guint64) a->raw[2]) << 16) |
+ (((guint64) a->raw[3]) << 24) |
+ (((guint64) a->raw[4]) << 32) |
+ (((guint64) a->raw[5]) << 40);
+
+ if (!strcmp(a->name, "spin-up-time"))
+ a->pretty_value = fourtyeight & 0xFFFF;
+ else if (!strcmp(a->name, "airflow-temperature-celsius") ||
+ !strcmp(a->name, "temperature-celsius-1") ||
+ !strcmp(a->name, "temperature-celsius-2")) {
+ a->pretty_value = (fourtyeight & 0xFFFF) + 273;
+ } else if (!strcmp(a->name, "power-on-minutes"))
+ a->pretty_value = fourtyeight * 60 * 1000;
+ else if (!strcmp(a->name, "power-on-seconds"))
+ a->pretty_value = fourtyeight * 1000;
+ else if (!strcmp(a->name, "power-on-hours") ||
+ !strcmp(a->name, "loaded-hours") ||
+ !strcmp(a->name, "head-flying-hours"))
+ a->pretty_value = fourtyeight * 60 * 60 * 1000;
+ else
+ a->pretty_value = fourtyeight;
}
-static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, guint8 id, SkSmartAttributeInfo *space) {
- const SkIdentifyParsedData *ipd;
+static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, guint8 id) {
+ const SkIdentifyParsedData *ipd;
- /* These are the simple cases */
- if (attribute_info[id].name)
- return &attribute_info[id];
+ /* These are the simple cases */
+ if (attribute_info[id].name)
+ return &attribute_info[id];
+
+ /* These are the complex ones */
+ if (sk_disk_identify_parse(d, &ipd) < 0)
+ return NULL;
+
+ switch (id) {
+ /* We might want to add further special cases/quirks
+ * here eventually. */
+
+ case 9: {
+
+ static const SkSmartAttributeInfo maxtor = {
+ "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
+ };
+ static const SkSmartAttributeInfo fujitsu = {
+ "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
+ };
+ static const SkSmartAttributeInfo others = {
+ "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS
+ };
+
+ if (strstr(ipd->model, "Maxtor") || strstr(ipd->model, "MAXTOR"))
+ return &maxtor;
+ else if (strstr(ipd->model, "Fujitsu") || strstr(ipd->model, "FUJITSU"))
+ return &fujitsu;
+
+ return &others;
+ }
+ }
- /* These are the complex ones */
- if (sk_disk_identify_parse(d, &ipd) < 0)
return NULL;
-
- switch (id) {
- case 9:
-
- if (strstr(ipd->model, "Maxtor"))
- space->name = "power-on-minutes";
- else if (strstr(ipd->model, "Fujitsu") || strstr(ipd->model, "FUJITSU"))
- space->name = "power-on-seconds";
- else
- space->name = "power-on-hours";
-
- space->unit = SK_SMART_ATTRIBUTE_UNIT_MSECONDS;
-
- return space;
- }
-
- return NULL;
}
int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) {
- if (!d->smart_data_valid) {
- errno = ENOENT;
- return -1;
- }
+ if (!d->smart_data_valid) {
+ errno = ENOENT;
+ return -1;
+ }
- switch (d->smart_data[362]) {
- case 0x00:
- case 0x80:
- d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
- break;
+ switch (d->smart_data[362]) {
+ case 0x00:
+ case 0x80:
+ d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER;
+ break;
+
+ case 0x02:
+ case 0x82:
+ d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
+ break;
+
+ case 0x03:
+ d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
+ break;
+
+ case 0x04:
+ case 0x84:
+ d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
+ break;
+
+ case 0x05:
+ case 0x85:
+ d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
+ break;
+
+ case 0x06:
+ case 0x86:
+ d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
+ break;
+
+ default:
+ d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
+ break;
+ }
- case 0x02:
- case 0x82:
- d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS;
- break;
+ d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
+ d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF;
- case 0x03:
- d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS;
- break;
+ d->smart_parsed_data.total_offline_data_collection_seconds = (guint16) d->smart_data[364] | ((guint16) d->smart_data[365] << 8);
- case 0x04:
- case 0x84:
- d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED;
- break;
+ d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d);
+ d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d);
+ d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d);
+ d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d);
- case 0x05:
- case 0x85:
- d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED;
- break;
+ d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
+ d->smart_parsed_data.extended_test_polling_minutes = d->smart_data[373] != 0xFF ? d->smart_data[373] : ((guint16) d->smart_data[376] << 8 | (guint16) d->smart_data[375]);
+ d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
- case 0x06:
- case 0x86:
- d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL;
- break;
+ *spd = &d->smart_parsed_data;
- default:
- d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN;
- break;
- }
+ return 0;
+}
- d->smart_parsed_data.selftest_execution_percent_remaining = 10*(d->smart_data[363] & 0xF);
+static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) {
+ guint8 *p;
+ unsigned n;
- d->smart_parsed_data.total_offline_data_collection_seconds = (guint16) d->smart_data[364] | ((guint16) d->smart_data[365] << 8);
+ if (!d->smart_threshold_data_valid) {
+ a->threshold_valid = FALSE;
+ return;
+ }
- d->smart_parsed_data.conveyance_test_available = !!(d->smart_data[367] & 32);
- d->smart_parsed_data.short_and_extended_test_available = !!(d->smart_data[367] & 16);
- d->smart_parsed_data.start_test_available = !!(d->smart_data[367] & 1);
- d->smart_parsed_data.abort_test_available = !!(d->smart_data[367] & 41);
+ for (n = 0, p = d->smart_threshold_data+2; n < 30; n++, p+=12)
+ if (p[0] == a->id)
+ break;
- d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372];
- d->smart_parsed_data.extended_test_polling_minutes = d->smart_data[373] != 0xFF ? d->smart_data[373] : ((guint16) d->smart_data[376] << 8 | (guint16) d->smart_data[375]);
- d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374];
+ if (n >= 30) {
+ a->threshold_valid = FALSE;
+ return;
+ }
- *spd = &d->smart_parsed_data;
+ a->threshold = p[1];
+ a->threshold_valid = TRUE;
- return 0;
+ a->bad =
+ a->worst_value <= a->threshold ||
+ a->current_value <= a->threshold;
}
-static void find_threshold(SkDisk *d, SkSmartAttribute *a) {
- guint8 *p;
- unsigned n;
-
- if (!d->smart_threshold_data_valid) {
- a->threshold_valid = FALSE;
- return;
- }
-
- for (n = 0, p = d->smart_threshold_data+2; n < 30; n++, p+=12)
- if (p[0] == a->id)
- break;
+int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, gpointer userdata) {
+ guint8 *p;
+ unsigned n;
- if (n >= 30) {
- a->threshold_valid = FALSE;
- return;
- }
+ if (!d->smart_data_valid) {
+ errno = ENOENT;
+ return -1;
+ }
- a->threshold = p[1];
- a->threshold_valid = TRUE;
+ for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
+ SkSmartAttributeParsedData a;
+ const SkSmartAttributeInfo *i;
+ gchar *an = NULL;
- a->bad =
- a->worst_value <= a->threshold ||
- a->current_value <= a->threshold;
-}
+ if (p[0] == 0)
+ continue;
-int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeCallback cb, gpointer userdata) {
- guint8 *p;
- unsigned n;
+ memset(&a, 0, sizeof(a));
+ a.id = p[0];
+ a.current_value = p[3];
+ a.worst_value = p[4];
- if (!d->smart_data_valid) {
- errno = ENOENT;
- return -1;
- }
+ a.flags = ((guint16) p[2] << 8) | p[1];
+ a.prefailure = !!(p[1] & 1);
+ a.online = !!(p[1] & 2);
- for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) {
- SkSmartAttribute a;
- SkSmartAttributeInfo space;
- const SkSmartAttributeInfo *i;
+ memcpy(a.raw, p+5, 6);
- if (p[0] == 0)
- continue;
+ if ((i = lookup_attribute(d, p[0]))) {
+ a.name = i->name;
+ a.pretty_unit = i->unit;
+ } else {
+ a.name = an = g_strdup_printf("attribute-%u", a.id);
+ a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN;
+ }
- memset(&a, 0, sizeof(a));
- a.id = p[0];
- a.current_value = p[3];
- a.worst_value = p[4];
+ make_pretty(&a);
- a.flag = p[2];
- a.prefailure = !!(p[1] & 1);
- a.online = !!(p[1] & 2);
+ find_threshold(d, &a);
- memcpy(a.raw, p+5, 6);
+ cb(d, &a, userdata);
- if ((i = lookup_attribute(d, p[0], &space))) {
- a.name = i->name;
- a.pretty_unit = i->unit;
+ g_free(an);
}
- make_pretty(&a);
-
- find_threshold(d, &a);
-
- if (cb)
- cb(d, &a, userdata);
- }
-
- return 0;
+ return 0;
}
static const char *yes_no(gboolean b) {
- return b ? "yes" : "no";
+ return b ? "yes" : "no";
}
const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) {
- const char * const map[] = {
- [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
- [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
- [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
- [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
- [SK_SMART_ATTRIBUTE_UNIT_KELVIN] = "K"
- };
+ const char * const map[] = {
+ [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL,
+ [SK_SMART_ATTRIBUTE_UNIT_NONE] = "",
+ [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms",
+ [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors",
+ [SK_SMART_ATTRIBUTE_UNIT_KELVIN] = "K"
+ };
- if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
- return NULL;
+ if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX)
+ return NULL;
- return map[unit];
+ return map[unit];
}
static char* print_name(char *s, size_t len, guint8 id, const char *k) {
- if (k)
- g_strlcpy(s, k, len);
- else
- g_snprintf(s, len, "%u", id);
+ if (k)
+ g_strlcpy(s, k, len);
+ else
+ g_snprintf(s, len, "%u", id);
- return s;
+ return s;
}
-static char *print_value(char *s, size_t len, const SkSmartAttribute *a) {
+static char *print_value(char *s, size_t len, const SkSmartAttributeParsedData *a) {
- switch (a->pretty_unit) {
- case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
+ switch (a->pretty_unit) {
+ case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
- if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
- g_snprintf(s, len, "%0.1f years", ((double) a->pretty_value)/(1000.0*60*60*24*365));
- else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
- g_snprintf(s, len, "%0.1f months", ((double) a->pretty_value)/(1000.0*60*60*24*30));
- else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
- g_snprintf(s, len, "%0.1f days", ((double) a->pretty_value)/(1000.0*60*60*24));
- else if (a->pretty_value >= 1000LLU*60LLU*60LLU)
- g_snprintf(s, len, "%0.1f h", ((double) a->pretty_value)/(1000.0*60*60));
- else if (a->pretty_value >= 1000LLU*60LLU)
- g_snprintf(s, len, "%0.1f min", ((double) a->pretty_value)/(1000.0*60));
- else if (a->pretty_value >= 1000LLU)
- g_snprintf(s, len, "%0.1f s", ((double) a->pretty_value)/(1000.0));
- else
- g_snprintf(s, len, "%llu ms", (unsigned long long) a->pretty_value);
+ if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
+ g_snprintf(s, len, "%0.1f years", ((double) a->pretty_value)/(1000.0*60*60*24*365));
+ else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
+ g_snprintf(s, len, "%0.1f months", ((double) a->pretty_value)/(1000.0*60*60*24*30));
+ else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
+ g_snprintf(s, len, "%0.1f days", ((double) a->pretty_value)/(1000.0*60*60*24));
+ else if (a->pretty_value >= 1000LLU*60LLU*60LLU)
+ g_snprintf(s, len, "%0.1f h", ((double) a->pretty_value)/(1000.0*60*60));
+ else if (a->pretty_value >= 1000LLU*60LLU)
+ g_snprintf(s, len, "%0.1f min", ((double) a->pretty_value)/(1000.0*60));
+ else if (a->pretty_value >= 1000LLU)
+ g_snprintf(s, len, "%0.1f s", ((double) a->pretty_value)/(1000.0));
+ else
+ g_snprintf(s, len, "%llu ms", (unsigned long long) a->pretty_value);
- break;
+ break;
- case SK_SMART_ATTRIBUTE_UNIT_KELVIN:
+ case SK_SMART_ATTRIBUTE_UNIT_KELVIN:
- g_snprintf(s, len, "%lli C", (long long) a->pretty_value - 273);
- break;
+ g_snprintf(s, len, "%lli C", (long long) a->pretty_value - 273);
+ break;
- case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
- g_snprintf(s, len, "%llu sectors", (unsigned long long) a->pretty_value);
- break;
+ case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
+ g_snprintf(s, len, "%llu sectors", (unsigned long long) a->pretty_value);
+ break;
- case SK_SMART_ATTRIBUTE_UNIT_NONE:
- g_snprintf(s, len, "%llu", (unsigned long long) a->pretty_value);
- break;
+ case SK_SMART_ATTRIBUTE_UNIT_NONE:
+ g_snprintf(s, len, "%llu", (unsigned long long) a->pretty_value);
+ break;
- case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
- g_snprintf(s, len, "n/a");
- break;
+ case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
+ g_snprintf(s, len, "n/a");
+ break;
- case _SK_SMART_ATTRIBUTE_UNIT_MAX:
- g_assert_not_reached();
- }
+ case _SK_SMART_ATTRIBUTE_UNIT_MAX:
+ g_assert_not_reached();
+ }
- return s;
+ return s;
};
-static void disk_dump_attributes(SkDisk *d, const SkSmartAttribute *a, gpointer userdata) {
- char name[32];
- char pretty[32];
- char t[32];
-
- g_snprintf(t, sizeof(t), "%3u", a->threshold);
-
- g_print("%3u %-27s %3u %3u %-3s %-11s %-7s %-7s %-3s\n",
- a->id,
- print_name(name, sizeof(name), a->id, a->name),
- a->current_value,
- a->worst_value,
- a->threshold_valid ? t : "n/a",
- print_value(pretty, sizeof(pretty), a),
- a->prefailure ? "prefail" : "old-age",
- a->online ? "online" : "offline",
- yes_no(!a->bad));
+#define HIGHLIGHT "\x1B[1m"
+#define ENDHIGHLIGHT "\x1B[0m"
+
+static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, gpointer userdata) {
+ char name[32];
+ char pretty[32];
+ char t[32];
+
+ g_snprintf(t, sizeof(t), "%3u", a->threshold);
+
+ if (a->bad && isatty(1))
+ fprintf(stderr, HIGHLIGHT);
+
+ g_print("%3u %-27s %3u %3u %-3s %-11s %-7s %-7s %-3s\n",
+ a->id,
+ print_name(name, sizeof(name), a->id, a->name),
+ a->current_value,
+ a->worst_value,
+ a->threshold_valid ? t : "n/a",
+ print_value(pretty, sizeof(pretty), a),
+ a->prefailure ? "prefail" : "old-age",
+ a->online ? "online" : "offline",
+ yes_no(!a->bad));
+
+ if (a->bad && isatty(1))
+ fprintf(stderr, ENDHIGHLIGHT);
}
int sk_disk_dump(SkDisk *d) {
- int ret;
- gboolean powered = FALSE;
-
- g_print("Device: %s\n"
- "Size: %lu MiB\n",
- d->name,
- (unsigned long) (d->size/1024/1024));
+ int ret;
+ gboolean awake = FALSE;
+
+ g_print("Device: %s\n"
+ "Size: %lu MiB\n",
+ d->name,
+ (unsigned long) (d->size/1024/1024));
+
+ if (d->identify_data_valid) {
+ const SkIdentifyParsedData *ipd;
+
+ if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
+ return ret;
+
+ g_print("Model: [%s]\n"
+ "Serial: [%s]\n"
+ "Firmware: [%s]\n"
+ "SMART Available: %s\n",
+ ipd->model,
+ ipd->serial,
+ ipd->firmware,
+ yes_no(disk_smart_is_available(d)));
+ }
- if (d->identify_data_valid) {
- const SkIdentifyParsedData *ipd;
+ ret = sk_disk_check_sleep_mode(d, &awake);
+ g_print("Awake: %s\n",
+ ret >= 0 ? yes_no(awake) : "unknown");
+
+ if (disk_smart_is_available(d)) {
+ const SkSmartParsedData *spd;
+
+ if ((ret = sk_disk_smart_read_data(d)) < 0)
+ return ret;
+
+ if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
+ return ret;
+
+ g_print("Off-line Data Collection Status: [%s]\n"
+ "Total Time To Complete Off-Line Data Collection: %u s\n"
+ "Self-Test Execution Status: [%s]\n"
+ "Percent Self-Test Remaining: %u%%\n"
+ "Conveyance Self-Test Available: %s\n"
+ "Short/Extended Self-Test Available: %s\n"
+ "Start Self-Test Available: %s\n"
+ "Abort Self-Test Available: %s\n"
+ "Short Self-Test Polling Time: %u min\n"
+ "Extended Self-Test Polling Time: %u min\n"
+ "Conveyance Self-Test Polling Time: %u min\n",
+ sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
+ spd->total_offline_data_collection_seconds,
+ sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status),
+ spd->self_test_execution_percent_remaining,
+ yes_no(spd->conveyance_test_available),
+ yes_no(spd->short_and_extended_test_available),
+ yes_no(spd->start_test_available),
+ yes_no(spd->abort_test_available),
+ spd->short_test_polling_minutes,
+ spd->extended_test_polling_minutes,
+ spd->conveyance_test_polling_minutes);
+
+ g_print("%3s %-27s %5s %5s %5s %-11s %-7s %-7s %-3s\n",
+ "ID#",
+ "Name",
+ "Value",
+ "Worst",
+ "Thres",
+ "Pretty",
+ "Type",
+ "Updates",
+ "Good");
+
+ if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
+ return ret;
+ }
- if ((ret = sk_disk_identify_parse(d, &ipd)) < 0)
- return ret;
-
- g_print("Model: [%s]\n"
- "Serial: [%s]\n"
- "Firmware: [%s]\n"
- "SMART Available: %s\n",
- ipd->model,
- ipd->serial,
- ipd->firmware,
- yes_no(disk_smart_is_available(d)));
- }
-
- ret = sk_disk_check_power_mode(d, &powered);
- g_print("Spin-up: %s\n",
- ret >= 0 ? yes_no(powered) : "unknown");
-
- if (disk_smart_is_available(d)) {
- const SkSmartParsedData *spd;
-
- if ((ret = sk_disk_smart_read_data(d)) < 0)
- return ret;
-
- if ((ret = sk_disk_smart_parse(d, &spd)) < 0)
- return ret;
-
- g_print("Off-line Collection Status: %s\n"
- "Percent Self-Test Remaining: %u%%\n"
- "Total Time To Complete Off-Line Data Collection: %u s\n"
- "Conveyance Self-Test Available: %s\n"
- "Short/Extended Self-Test Available: %s\n"
- "Start Self-Test Available: %s\n"
- "Abort Self-Test Available: %s\n",
- sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status),
- spd->selftest_execution_percent_remaining,
- spd->total_offline_data_collection_seconds,
- yes_no(spd->conveyance_test_available),
- yes_no(spd->short_and_extended_test_available),
- yes_no(spd->start_test_available),
- yes_no(spd->abort_test_available));
-
- if (spd->short_and_extended_test_available)
- g_print("Short Self-Test Polling Time: %u min\n"
- "Extended Self-Test Polling Time: %u min\n",
- spd->short_test_polling_minutes,
- spd->extended_test_polling_minutes);
-
- if (spd->conveyance_test_available)
- g_print("Conveyance Self-Test Polling Time: %u min\n",
- spd->conveyance_test_polling_minutes);
-
- g_print("%3s %-27s %5s %5s %5s %-11s %-7s %-7s %-3s\n",
- "ID#",
- "Name",
- "Value",
- "Worst",
- "Thres",
- "Pretty",
- "Type",
- "Updates",
- "Good");
-
- if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0)
- return ret;
- }
-
- return 0;
+ return 0;
}
int sk_disk_get_size(SkDisk *d, guint64 *bytes) {
- *bytes = d->size;
- return 0;
+ *bytes = d->size;
+ return 0;
}
int sk_disk_open(const gchar *name, SkDisk **_d) {
- SkDisk *d;
- int ret = -1;
+ SkDisk *d;
+ int ret = -1;
+ struct stat st;
- g_assert(name);
- g_assert(_d);
+ g_assert(name);
+ g_assert(_d);
- d = g_new0(SkDisk, 1);
- d->name = g_strdup(name);
+ d = g_new0(SkDisk, 1);
+ d->name = g_strdup(name);
- if ((d->fd = open(name, O_RDWR|O_NOCTTY)) < 0) {
- ret = d->fd;
- goto fail;
- }
-
- if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
- goto fail;
-
- if (d->size <= 0 || d->size == (guint64) -1) {
- errno = EINVAL;
- goto fail;
- }
-
- /* Find a way to identify the device */
- for (d->type = 0; d->type != SK_DISK_TYPE_UNKNOWN; d->type++)
- if (disk_identify_device(d) >= 0)
- break;
+ if ((d->fd = open(name, O_RDWR|O_NOCTTY)) < 0) {
+ ret = d->fd;
+ goto fail;
+ }
- /* Check if driver can do SMART, and enable if necessary */
- if (disk_smart_is_available(d)) {
+ if ((ret = fstat(d->fd, &st)) < 0)
+ goto fail;
- if (!disk_smart_is_enabled(d)) {
- if ((ret = disk_smart_enable(d, TRUE)) < 0)
+ if (!S_ISBLK(st.st_mode)) {
+ errno = ENODEV;
goto fail;
+ }
+
+ /* So, it's a block device. Let's make sure the ioctls work */
- if ((ret = disk_identify_device(d)) < 0)
+ if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0)
goto fail;
- if (!disk_smart_is_enabled(d)) {
+ if (d->size <= 0 || d->size == (guint64) -1) {
errno = EIO;
- ret = -1;
goto fail;
- }
}
- disk_smart_read_thresholds(d);
- }
+ /* OK, it's a real block device with a size. Find a way to
+ * identify the device. */
+ for (d->type = 0; d->type != SK_DISK_TYPE_UNKNOWN; d->type++)
+ if (disk_identify_device(d) >= 0)
+ break;
- *_d = d;
+ /* Check if driver can do SMART, and enable if necessary */
+ if (disk_smart_is_available(d)) {
- return 0;
+ if (!disk_smart_is_enabled(d)) {
+ if ((ret = disk_smart_enable(d, TRUE)) < 0)
+ goto fail;
+
+ if ((ret = disk_identify_device(d)) < 0)
+ goto fail;
+
+ if (!disk_smart_is_enabled(d)) {
+ errno = EIO;
+ ret = -1;
+ goto fail;
+ }
+ }
+
+ disk_smart_read_thresholds(d);
+ }
+
+ *_d = d;
+
+ return 0;
fail:
- if (d)
- sk_disk_free(d);
+ if (d)
+ sk_disk_free(d);
- return ret;
+ return ret;
}
void sk_disk_free(SkDisk *d) {
- g_assert(d);
+ g_assert(d);
- if (d->fd >= 0)
- close(d->fd);
+ if (d->fd >= 0)
+ close(d->fd);
- g_free(d->name);
- g_free(d);
+ g_free(d->name);
+ g_free(d);
}
diff --git a/smart.h b/smart.h
index cc96efb..efdc4be 100644
--- a/smart.h
+++ b/smart.h
@@ -1,99 +1,170 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
#ifndef foosmarthfoo
#define foosmarthfoo
+/***
+ This file is part of SmartKit.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
#include <glib.h>
-typedef struct SkDisk SkDisk;
+/* ATA SMART test type (ATA8 7.52.5.2) */
+typedef enum SkSmartSelfTest {
+ SK_SMART_SELF_TEST_SHORT = 1,
+ SK_SMART_SELF_TEST_EXTENDED = 2,
+ SK_SMART_SELF_TEST_CONVEYANCE = 3,
+ SK_SMART_SELF_TEST_ABORT = 127
+} SkSmartSelfTest;
+
+const char* sk_smart_self_test_to_string(SkSmartSelfTest test);
typedef struct SkIdentifyParsedData {
- gchar serial[21];
- gchar firmware[9];
- gchar model[41];
+ gchar serial[21];
+ gchar firmware[9];
+ gchar model[41];
+
+ /* This structure may be extended at any time without this being
+ * considered an ABI change. So take care when you copy it. */
} SkIdentifyParsedData;
typedef enum SkSmartOfflineDataCollectionStatus {
- SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER,
- SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS,
- SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS,
- SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED,
- SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED,
- SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL,
- SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN,
- _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX
+ SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER,
+ SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS,
+ SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS,
+ SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED,
+ SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED,
+ SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL,
+ SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN,
+ _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX
} SkSmartOfflineDataCollectionStatus;
-typedef struct SkSmartParsedData {
- SkSmartOfflineDataCollectionStatus offline_data_collection_status;
- unsigned selftest_execution_percent_remaining;
- unsigned total_offline_data_collection_seconds;
-
- gboolean conveyance_test_available:1;
- gboolean short_and_extended_test_available:1;
- gboolean start_test_available:1;
- gboolean abort_test_available:1;
+const char* sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status);
- unsigned short_test_polling_minutes;
- unsigned extended_test_polling_minutes;
- unsigned conveyance_test_polling_minutes;
+typedef enum SkSmartSelfTestExecutionStatus {
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER = 0,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED = 1,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED = 2,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL = 3,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN = 4,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL = 5,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO = 6,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ = 7,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING = 8,
+ SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS = 15,
+ _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX
+} SkSmartSelfTestExecutionStatus;
+
+const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status);
- /* This structure may be extended at any time without being
- * considered an ABI change. So take care when you copy it. */
+typedef struct SkSmartParsedData {
+ /* Volatile data */
+ SkSmartOfflineDataCollectionStatus offline_data_collection_status;
+ unsigned total_offline_data_collection_seconds;
+ SkSmartSelfTestExecutionStatus self_test_execution_status;
+ unsigned self_test_execution_percent_remaining;
+
+ /* Fixed data */
+ gboolean short_and_extended_test_available:1;
+ gboolean conveyance_test_available:1;
+ gboolean start_test_available:1;
+ gboolean abort_test_available:1;
+
+ unsigned short_test_polling_minutes;
+ unsigned extended_test_polling_minutes;
+ unsigned conveyance_test_polling_minutes;
+
+ /* This structure may be extended at any time without this being
+ * considered an ABI change. So take care when you copy it. */
} SkSmartParsedData;
+gboolean sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test);
+unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test);
+
typedef enum SkSmartAttributeUnit {
- SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,
- SK_SMART_ATTRIBUTE_UNIT_NONE,
- SK_SMART_ATTRIBUTE_UNIT_MSECONDS,
- SK_SMART_ATTRIBUTE_UNIT_SECTORS,
- SK_SMART_ATTRIBUTE_UNIT_KELVIN,
- _SK_SMART_ATTRIBUTE_UNIT_MAX
+ SK_SMART_ATTRIBUTE_UNIT_UNKNOWN,
+ SK_SMART_ATTRIBUTE_UNIT_NONE,
+ SK_SMART_ATTRIBUTE_UNIT_MSECONDS,
+ SK_SMART_ATTRIBUTE_UNIT_SECTORS,
+ SK_SMART_ATTRIBUTE_UNIT_KELVIN,
+ _SK_SMART_ATTRIBUTE_UNIT_MAX
} SkSmartAttributeUnit;
-typedef struct SkSmartAttribute {
- /* Static data */
- guint8 id;
- const char *name;
- SkSmartAttributeUnit pretty_unit; /* for pretty value */
+const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit);
+
+typedef struct SkSmartAttributeParsedData {
+ /* Fixed data */
+ guint8 id;
+ const char *name;
+ SkSmartAttributeUnit pretty_unit; /* for pretty_value */
- guint8 threshold;
- gboolean threshold_valid:1;
+ guint16 flags;
- gboolean online:1;
- gboolean prefailure:1;
+ guint8 threshold;
+ gboolean threshold_valid:1;
- guint8 flag;
+ gboolean online:1;
+ gboolean prefailure:1;
- /* Volatile data */
- gboolean bad:1;
- guint8 current_value, worst_value;
- guint64 pretty_value;
- guint8 raw[6];
+ /* Volatile data */
+ gboolean bad:1;
+ guint8 current_value, worst_value;
+ guint64 pretty_value;
+ guint8 raw[6];
- /* This structure may be extended at any time without being
- * considered an ABI change. So take care when you copy it. */
-} SkSmartAttribute;
+ /* This structure may be extended at any time without this being
+ * considered an ABI change. So take care when you copy it. */
+} SkSmartAttributeParsedData;
-typedef void (*SkSmartAttributeCallback)(SkDisk *d, const SkSmartAttribute *a, gpointer userdata);
+typedef struct SkDisk SkDisk;
int sk_disk_open(const gchar *name, SkDisk **d);
-int sk_disk_check_power_mode(SkDisk *d, gboolean *mode);
+int sk_disk_check_sleep_mode(SkDisk *d, gboolean *awake);
int sk_disk_identify_is_available(SkDisk *d, gboolean *b);
int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **data);
int sk_disk_smart_is_available(SkDisk *d, gboolean *b);
+
+/* Reading SMART data might cause the disk to wake up from
+ * sleep. Hence from monitoring daemons make sure to call
+ * sk_disk_check_power_mode() to check wether the disk is sleeping and
+ * skip the read if so. */
int sk_disk_smart_read_data(SkDisk *d);
+
int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **data);
-int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeCallback cb, gpointer userdata);
+
+typedef void (*SkSmartAttributeParseCallback)(SkDisk *d, const SkSmartAttributeParsedData *a, gpointer userdata);
+int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, gpointer userdata);
int sk_disk_get_size(SkDisk *d, guint64 *bytes);
+int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test);
+
int sk_disk_dump(SkDisk *d);
void sk_disk_free(SkDisk *d);
-const char* sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status);
-const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit);
+/* TODO:
+ *
+ * Smart status
+ */
#endif
diff --git a/smart.vapi b/smart.vapi
new file mode 100644
index 0000000..e92215f
--- /dev/null
+++ b/smart.vapi
@@ -0,0 +1,139 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ This file is part of SmartKit.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+using GLib;
+
+[CCode (cheader_filename="smart.h")]
+namespace Smart {
+
+ [CCode (cname="SkSmartSelfTest", cprefix="SK_SMART_SELF_TEST_")]
+ public enum SmartSelfTest {
+ SHORT, EXTENDED, CONVEYANCE, ABORT
+ }
+
+ [Immutable]
+ [CCode (cname="SkIdentifyParsedData")]
+ public struct IdentifyParsedData {
+ public string serial;
+ public string firmware;
+ public string model;
+ }
+
+ [CCode (cname="SkSmartOfflineDataCollectionStatus", cprefix="SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_")]
+ public enum SmartOfflineDataCollectionStatus {
+ NEVER, SUCCESS, INPROGRESS, SUSPENDED, ABORTED, FATAL, UNKNOWN
+ }
+
+ [CCode (cname="sk_smart_offline_data_collection_status_to_string")]
+ public weak string smart_offline_data_collection_status_to_string(SmartOfflineDataCollectionStatus status);
+
+
+ [CCode (cname="SkSmartSelfTestExecutionStatus", cprefix="SK_SMART_SELF_TEST_EXECUTION_STATUS_")]
+ public enum SmartSelfTestExecutionStatus {
+ SUCCESS_OR_NEVER, ABORTED, INTERRUPTED, FATAL, ERROR_UNKNOWN, ERROR_ELECTRICAL, ERROR_SERVO, ERROR_READ, ERROR_HANDLING, INPROGRESS
+ }
+
+ [CCode (cname="sk_smart_self_test_execution_status_to_string")]
+ public weak string smart_self_test_execution_status_to_string(SmartSelfTestExecutionStatus status);
+
+ [Immutable]
+ [CCode (cname="SkSmartParsedData")]
+ public struct SmartParsedData {
+ public SmartOfflineDataCollectionStatus offline_data_collection_status;
+ public uint total_offline_data_collection_seconds;
+ public SmartSelfTestExecutionStatus self_test_execution_status;
+ public uint self_test_execution_percent_remaining;
+
+ public bool conveyance_test_available;
+ public bool short_and_extended_test_available;
+ public bool start_test_available;
+ public bool abort_test_available;
+
+ public uint short_test_polling_minutes;
+ public uint extended_test_polling_minutes;
+ public uint conveyance_test_polling_minutes;
+
+ [CCode (cname="sk_smart_self_test_available")]
+ public bool self_test_available(SmartSelfTest test);
+
+ [CCode (cname="sk_smart_self_test_polling_minutes")]
+ public uint self_test_polling_minutes(SmartSelfTest test);
+ }
+
+ [CCode (cname="SkSmartAttributeUnit", cprefix="CK_SMART_ATTRIBUTE_UNIT")]
+ public enum SmartAttributeUnit {
+ UNKNOWN, NONE, MSECONDS, SECTORS, KELVIN
+ }
+
+ [CCode (cname="sk_smart_attribute_unit_to_string")]
+ public weak string smart_attribute_unit_to_string(SmartAttributeUnit unit);
+
+ [Immutable]
+ [CCode (cname="SkSmartAttribute")]
+ public struct SmartAttribute {
+ public uint8 id;
+ public char *name;
+ public SmartAttributeUnit pretty_unit;
+ public uint16 flags;
+ public uint8 threshold;
+ public bool threshold_valid;
+ public bool online;
+ public bool prefailure;
+ public bool bad;
+ public uint8 current_value;
+ public uint8 worst_value;
+ public uint64 pretty_value;
+ public uint8[6] raw;
+ }
+
+ [Compact]
+ [CCode (free_function="sk_disk_free", cname="SkDisk", cprefix="sk_disk_")]
+ public class Disk {
+
+ public delegate void SmartAttributeCallback(Disk d, SmartAttribute a, void* userdata);
+
+ public static int open(string name, out Disk disk);
+
+ public int check_sleep_mode(out bool awake);
+
+ public int identify_is_available(out bool mode);
+ public int identify_parse(out weak IdentifyParsedData* data);
+
+ public int smart_is_available(out bool mode);
+ public int smart_read_data();
+ public int smart_parse_attributes(SmartAttributeCallback cb, void* userdata);
+ public int smart_parse(out weak SmartParsedData* data);
+
+ public int get_size(out uint64 bytes);
+
+ public int self_test(SmartSelfTest test);
+
+ public int dump();
+ }
+
+ /* These two should move to an official vala package */
+ [CCode (cname="errno", cheader_filename="errno.h")]
+ public int errno;
+
+ [CCode (cname="g_strerror", cheader_filename="glib.h")]
+ public weak string strerror(int err);
+}
diff --git a/smartkitd.vala b/smartkitd.vala
new file mode 100644
index 0000000..0ef72cf
--- /dev/null
+++ b/smartkitd.vala
@@ -0,0 +1,390 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+ This file is part of SmartKit.
+
+ Copyright 2008 Lennart Poettering
+
+ libcanberra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libcanberra 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libcanberra. If not, If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+using GLib;
+using DBus;
+using Hal;
+using Smart;
+
+errordomain Error {
+ SMART_NOT_AVAILABLE,
+ SYSTEM,
+ NOT_FOUND
+}
+
+[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; */
+}
+
+[DBus (name = "net.poettering.SmartKit.Disk")]
+public interface DiskAPI {
+
+/* public abstract uint64 size { get; } */
+
+ public abstract string getPath() throws Error;
+ public abstract string getUDI() throws Error;
+
+ public abstract uint64 getSize() throws Error;
+
+ public abstract bool checkPowerMode() throws Error;
+
+ public abstract bool isIdentifyAvailable() throws Error;
+ public abstract string getIdentifySerial() throws Error;
+ public abstract string getIdentifyFirmware() throws Error;
+ public abstract string getIdentifyModel() throws Error;
+
+ public abstract bool isSmartAvailable() throws Error;
+ public abstract void readSmartData() throws Error;
+
+ public abstract string getOfflineDataCollectionStatus() throws Error;
+ public abstract uint getSelfTestExecutionPercentRemaining() throws Error;
+ public abstract uint getTotalOfflineDataCollectionSeconds() throws Error;
+ public abstract bool getConveyanceTestAvailable() throws Error;
+ public abstract bool getShortAndExtendedTestAvailable() throws Error;
+ public abstract bool getStartTestAvailable() throws Error;
+ public abstract bool getAbortTestAvailable() throws Error;
+
+ public abstract uint getShortTestPollingMinutes() throws Error;
+ public abstract uint getExtendedTestPollingMinutes() throws Error;
+ public abstract uint getConveyanceTestPollingMinutes() throws Error;
+}
+
+public class Disk : GLib.Object, DiskAPI {
+ private Smart.Disk disk;
+ public string dbus_path;
+
+ public string path { get; construct; }
+ public string udi { get; construct; }
+ public DBus.Connection connection { get; construct; }
+
+ Disk(DBus.Connection connection, string path, string udi) {
+ this.connection = connection;
+ this.path = path;
+ this.udi = udi;
+ }
+
+ private string clean_path(string s) {
+ var builder = new StringBuilder ();
+ string t;
+
+ for (int i = 0; i < s.size(); i++)
+ if (s[i].isalnum() || s[i] == '_')
+ builder.append_unichar(s[i]);
+ else
+ builder.append_unichar('_');
+
+ return builder.str;
+ }
+
+ public void open() throws Error {
+ if (Smart.Disk.open(this.path, out this.disk) < 0)
+ throw new Error.SYSTEM("open() failed");
+
+ weak Smart.IdentifyParsedData *d;
+
+ if (this.disk.identify_parse(out d) >= 0)
+ this.dbus_path = "/disk/%s/%s".printf(clean_path(d->model), clean_path(d->serial));
+ else
+ this.dbus_path = "/disk/%s".printf(clean_path(this.path));
+
+ stderr.printf("Registering D-Bus path %s\n", this.dbus_path);
+ this.connection.register_object(this.dbus_path, this);
+
+ this.disk.smart_read_data();
+ }
+
+ public string getPath() throws Error {
+ return this.path;
+ }
+
+ public string getUDI() throws Error {
+ return this.udi;
+ }
+
+ public uint64 getSize() throws Error {
+ uint64 s;
+ if (this.disk.get_size(out s) < 0)
+ throw new Error.SYSTEM("get_size() failed: %s", Smart.strerror(Smart.errno));
+ return s;
+ }
+
+ public bool checkPowerMode() throws Error {
+ bool b;
+ if (this.disk.check_sleep_mode(out b) < 0)
+ throw new Error.SYSTEM("check_power_mode() failed: %s", Smart.strerror(Smart.errno));
+ return b;
+ }
+
+ public bool isIdentifyAvailable() throws Error {
+ bool b;
+ if (this.disk.identify_is_available(out b) < 0)
+ throw new Error.SYSTEM("identify_is_available() failed: %s", Smart.strerror(Smart.errno));
+ return b;
+ }
+
+ public string getIdentifySerial() throws Error {
+ weak Smart.IdentifyParsedData *d;
+ if (this.disk.identify_parse(out d) < 0)
+ throw new Error.SYSTEM("identify_parse() failed: %s", Smart.strerror(Smart.errno));
+ return d->serial;
+ }
+
+ public string getIdentifyFirmware() throws Error {
+ weak Smart.IdentifyParsedData *d;
+ if (this.disk.identify_parse(out d) < 0)
+ throw new Error.SYSTEM("identify_parse() failed: %s", Smart.strerror(Smart.errno));
+ return d->firmware;
+ }
+
+ public string getIdentifyModel() throws Error {
+ weak Smart.IdentifyParsedData *d;
+ if (this.disk.identify_parse(out d) < 0)
+ throw new Error.SYSTEM("identify_parse() failed: %s", Smart.strerror(Smart.errno));
+ return d->model;
+ }
+
+ public bool isSmartAvailable() throws Error {
+ bool b;
+ if (this.disk.smart_is_available(out b) < 0)
+ throw new Error.SYSTEM("smart_is_available() failed: %s", Smart.strerror(Smart.errno));
+ return b;
+ }
+
+ public void readSmartData() throws Error {
+ if (this.disk.smart_read_data() < 0)
+ throw new Error.SYSTEM("smart_read_data() failed: %s", Smart.strerror(Smart.errno));
+ }
+
+ public string getOfflineDataCollectionStatus() throws Error {
+ weak Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ switch (d->offline_data_collection_status) {
+ case SmartOfflineDataCollectionStatus.NEVER:
+ return "never";
+ case SmartOfflineDataCollectionStatus.SUCCESS:
+ return "success";
+ case SmartOfflineDataCollectionStatus.INPROGRESS:
+ return "inprogress";
+ case SmartOfflineDataCollectionStatus.SUSPENDED:
+ return "suspended";
+ case SmartOfflineDataCollectionStatus.ABORTED:
+ return "aborted";
+ case SmartOfflineDataCollectionStatus.FATAL:
+ return "fatal";
+ default:
+ return "unknown";
+ }
+ }
+
+ public uint getSelfTestExecutionPercentRemaining() throws Error {
+ weak Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->self_test_execution_percent_remaining;
+ }
+
+ public uint getTotalOfflineDataCollectionSeconds() throws Error {
+ weak Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->total_offline_data_collection_seconds;
+ }
+
+ public bool getConveyanceTestAvailable() throws Error {
+ weak Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->conveyance_test_available;
+ }
+
+ public bool getShortAndExtendedTestAvailable() throws Error {
+ weak Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->short_and_extended_test_available;
+ }
+
+ public bool getStartTestAvailable() throws Error {
+ Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->start_test_available;
+ }
+
+ public bool getAbortTestAvailable() throws Error {
+ Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->abort_test_available;
+ }
+
+ public uint getShortTestPollingMinutes() throws Error {
+ Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->short_test_polling_minutes;
+ }
+
+ public uint getExtendedTestPollingMinutes() throws Error {
+ Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->extended_test_polling_minutes;
+ }
+
+ public uint getConveyanceTestPollingMinutes() throws Error {
+ Smart.SmartParsedData *d;
+
+ if (this.disk.smart_parse(out d) < 0)
+ throw new Error.SYSTEM("smart_parse() failed: %s", Smart.strerror(Smart.errno));
+
+ return d->conveyance_test_polling_minutes;
+ }
+
+/* public uint64 size { */
+/* get { */
+/* uint64 s; */
+/* this.disk.get_size(out s); */
+/* return s; */
+
+/* } */
+/* } */
+}
+
+public class Manager : GLib.Object, ManagerAPI {
+ public DBus.Connection connection { get; construct; }
+ public List<Disk> disks;
+
+ public DBus.RawConnection raw_connection;
+ public Hal.Context hal_context;
+
+ Manager(DBus.Connection connection) {
+ this.connection = connection;
+ }
+
+ public void start() throws Error {
+ DBus.RawError err;
+
+ this.connection.register_object("/", this);
+
+ this.raw_connection = DBus.RawBus.get(DBus.BusType.SYSTEM, ref err);
+
+ this.hal_context = new Hal.Context();
+ this.hal_context.set_dbus_connection(this.raw_connection);
+
+ string[] haldisks = this.hal_context.find_device_by_capability("storage", ref err);
+
+ foreach (string udi in haldisks) {
+ string bdev = this.hal_context.device_get_property_string(udi, "block.device", ref err);
+
+ stderr.printf("Found device %s\n", bdev);
+
+ try {
+ Disk disk = new Disk(this.connection, bdev, udi);
+ disk.open();
+ this.disks.append(#disk);
+ } catch (Error e) {
+ stderr.printf("Failed to open disk %s: %s\n", bdev, e.message);
+ }
+ }
+ }
+
+ public DBus.ObjectPath getDiskByUDI(string udi) throws Error {
+
+ foreach (Disk d in this.disks)
+ if (d.udi == udi)
+ return new DBus.ObjectPath(d.dbus_path);
+
+ throw new Error.NOT_FOUND("Device not found");
+ }
+
+ public DBus.ObjectPath getDiskByPath(string path) throws Error {
+ foreach (Disk d in this.disks)
+ if (d.path == path)
+ return new DBus.ObjectPath(d.dbus_path);
+
+ throw new Error.NOT_FOUND("Device not found");
+ }
+
+/* public DBus.ObjectPath[] getDisks() throws Error { */
+/* DBus.ObjectPath[] o = new DBus.ObjectPath[this.disks.length()]; */
+
+/* int i = 0; */
+/* foreach (Disk d in this.disks) */
+/* o[i++] = new DBus.ObjectPath(d.dbus_path); */
+
+/* return o; */
+/* } */
+
+}
+
+
+
+int main() {
+
+ try {
+ var c = DBus.Bus.get(DBus.BusType.SYSTEM);
+
+ dynamic DBus.Object bus = c.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus");
+
+ uint request_name_result = bus.RequestName("net.poettering.SmartKit", (uint) 0);
+
+ if (request_name_result == DBus.RequestNameReply.PRIMARY_OWNER) {
+
+ MainLoop loop = new MainLoop(null, false);
+ Manager manager = new Manager(c);
+
+ manager.start();
+
+ stdout.printf("Started\n");
+ loop.run();
+ }
+
+ } catch (Error e) {
+ stderr.printf("Error: %s\n", e.message);
+ return 1;
+ }
+
+ return 0;
+}