diff options
| -rw-r--r-- | .gitignore | 8 | ||||
| -rw-r--r-- | Makefile | 18 | ||||
| -rw-r--r-- | gnome-disk-health.glade | 620 | ||||
| -rw-r--r-- | gnome-disk-health.vala | 137 | ||||
| -rw-r--r-- | skdump.c | 52 | ||||
| -rw-r--r-- | sktest.c | 66 | ||||
| -rw-r--r-- | smart.c | 1615 | ||||
| -rw-r--r-- | smart.h | 181 | ||||
| -rw-r--r-- | smart.vapi | 139 | ||||
| -rw-r--r-- | smartkitd.vala | 390 | 
10 files changed, 2436 insertions, 790 deletions
@@ -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 @@ -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"><b>Firmware:</b></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"><b>Serial Number:</b></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"><b>Model:</b></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"><b>Size:</b></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"><b>Path:</b></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"><b>Verdict:</b></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"><b>Type:</b></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"><b>Threshold:</b></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"><b>Worst Value:</b></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"><b>Current Value:</b></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"><b>ID:</b></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"><b>Name:</b></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; +        } +} @@ -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; +} @@ -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);  } @@ -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; +}  | 
