10507 lines
358 KiB
Diff
10507 lines
358 KiB
Diff
|
From 386480674962607c6166c2835f2ae3057800651f Mon Sep 17 00:00:00 2001
|
||
|
From: Tomas Bzatek <tbzatek@redhat.com>
|
||
|
Date: Thu, 29 Dec 2022 15:30:06 +0100
|
||
|
Subject: [PATCH] New SMART plugin
|
||
|
|
||
|
---
|
||
|
Makefile.am | 11 +-
|
||
|
configure.ac | 151 +-
|
||
|
data/conf.d/00-default.cfg | 3 +
|
||
|
dist/libblockdev.spec.in | 95 +-
|
||
|
docs/libblockdev-docs.xml.in | 1 +
|
||
|
docs/libblockdev-sections.txt | 39 +
|
||
|
include/blockdev/Makefile.am | 1 +
|
||
|
src/lib/Makefile.am | 3 +-
|
||
|
src/lib/blockdev.c.in | 20 +-
|
||
|
src/lib/plugin_apis/smart.api | 678 +++++++++
|
||
|
src/lib/plugins.h | 1 +
|
||
|
src/plugins/Makefile.am | 6 +
|
||
|
src/plugins/smart/Makefile.am | 54 +
|
||
|
src/plugins/smart/drivedb-parser.c | 216 +++
|
||
|
src/plugins/smart/libatasmart.c | 561 ++++++++
|
||
|
src/plugins/smart/smart-common.c | 175 +++
|
||
|
src/plugins/smart/smart-private.h | 104 ++
|
||
|
src/plugins/smart/smart.h | 460 ++++++
|
||
|
src/plugins/smart/smartmontools.c | 1252 +++++++++++++++++
|
||
|
src/python/gi/overrides/BlockDev.py | 8 +
|
||
|
src/utils/exec.c | 144 +-
|
||
|
src/utils/exec.h | 1 +
|
||
|
tests/fake_utils/smartctl/smartctl | 18 +
|
||
|
tests/run_tests.py | 2 +-
|
||
|
tests/smart_dumps/01_old_ver.json | 214 +++
|
||
|
tests/smart_dumps/02_exit_err.json | 214 +++
|
||
|
tests/smart_dumps/03_exit_err_32.json | 214 +++
|
||
|
tests/smart_dumps/04_malformed.json | 215 +++
|
||
|
tests/smart_dumps/05_empty.json | 0
|
||
|
tests/smart_dumps/Biwintech_SSD_SX500.bin | Bin 0 -> 1572 bytes
|
||
|
.../GIGABYTE_GP-GSTFS31100TNTD.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/HGST_HMS5C4040BLE640.json | 523 +++++++
|
||
|
tests/smart_dumps/HGST_HUS726060ALA640.json | 545 +++++++
|
||
|
tests/smart_dumps/HGST_HUSMR3280ASS200.json | 155 ++
|
||
|
.../smart_dumps/Hitachi_HDS5C3020ALA632.json | 519 +++++++
|
||
|
tests/smart_dumps/Hitachi_HDS721010CLA632.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/IBM_IC25N020ATCS04-0.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/INTEL_SSDSC2BB120G4L.json | 699 +++++++++
|
||
|
.../KINGSTON_SA400S37480G_SBFKQ13.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/Maxtor_6Y120P0.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/Patriot_Burst_240GB.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/SAMSUNG_HS122JC.bin | Bin 0 -> 1572 bytes
|
||
|
.../SAMSUNG_MMCRE28G5MXP-0VBH1.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/SEAGATE_ST600MP0036.json | 168 +++
|
||
|
.../smart_dumps/SiliconPower_SSD_SBFM61.3.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/TOSHIBA_AL15SEB120NY.json | 149 ++
|
||
|
tests/smart_dumps/TOSHIBA_AL15SEB18EQY.json | 150 ++
|
||
|
tests/smart_dumps/TOSHIBA_KPM5XMUG400G.json | 153 ++
|
||
|
tests/smart_dumps/TOSHIBA_THNSNH128GBST.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_dumps/TOSHIBA_THNSNH128GBST.json | 544 +++++++
|
||
|
tests/smart_dumps/WD4001FYYG-01SL3.json | 119 ++
|
||
|
tests/smart_dumps/WDC_WD10EFRX-68PJCN0.json | 518 +++++++
|
||
|
tests/smart_dumps/WDC_WD20EARS-00MVWB0.bin | Bin 0 -> 1572 bytes
|
||
|
tests/smart_test.py | 246 ++++
|
||
|
tests/smartmontools_test.py | 424 ++++++
|
||
|
tests/utils_test.py | 16 +
|
||
|
56 files changed, 9676 insertions(+), 113 deletions(-)
|
||
|
create mode 100644 src/lib/plugin_apis/smart.api
|
||
|
create mode 100644 src/plugins/smart/Makefile.am
|
||
|
create mode 100644 src/plugins/smart/drivedb-parser.c
|
||
|
create mode 100644 src/plugins/smart/libatasmart.c
|
||
|
create mode 100644 src/plugins/smart/smart-common.c
|
||
|
create mode 100644 src/plugins/smart/smart-private.h
|
||
|
create mode 100644 src/plugins/smart/smart.h
|
||
|
create mode 100644 src/plugins/smart/smartmontools.c
|
||
|
create mode 100755 tests/fake_utils/smartctl/smartctl
|
||
|
create mode 100644 tests/smart_dumps/01_old_ver.json
|
||
|
create mode 100644 tests/smart_dumps/02_exit_err.json
|
||
|
create mode 100644 tests/smart_dumps/03_exit_err_32.json
|
||
|
create mode 100644 tests/smart_dumps/04_malformed.json
|
||
|
create mode 100644 tests/smart_dumps/05_empty.json
|
||
|
create mode 100644 tests/smart_dumps/Biwintech_SSD_SX500.bin
|
||
|
create mode 100644 tests/smart_dumps/GIGABYTE_GP-GSTFS31100TNTD.bin
|
||
|
create mode 100644 tests/smart_dumps/HGST_HMS5C4040BLE640.json
|
||
|
create mode 100644 tests/smart_dumps/HGST_HUS726060ALA640.json
|
||
|
create mode 100644 tests/smart_dumps/HGST_HUSMR3280ASS200.json
|
||
|
create mode 100644 tests/smart_dumps/Hitachi_HDS5C3020ALA632.json
|
||
|
create mode 100644 tests/smart_dumps/Hitachi_HDS721010CLA632.bin
|
||
|
create mode 100644 tests/smart_dumps/IBM_IC25N020ATCS04-0.bin
|
||
|
create mode 100644 tests/smart_dumps/INTEL_SSDSC2BB120G4L.json
|
||
|
create mode 100644 tests/smart_dumps/KINGSTON_SA400S37480G_SBFKQ13.bin
|
||
|
create mode 100644 tests/smart_dumps/Maxtor_6Y120P0.bin
|
||
|
create mode 100644 tests/smart_dumps/Patriot_Burst_240GB.bin
|
||
|
create mode 100644 tests/smart_dumps/SAMSUNG_HS122JC.bin
|
||
|
create mode 100644 tests/smart_dumps/SAMSUNG_MMCRE28G5MXP-0VBH1.bin
|
||
|
create mode 100644 tests/smart_dumps/SEAGATE_ST600MP0036.json
|
||
|
create mode 100644 tests/smart_dumps/SiliconPower_SSD_SBFM61.3.bin
|
||
|
create mode 100644 tests/smart_dumps/TOSHIBA_AL15SEB120NY.json
|
||
|
create mode 100644 tests/smart_dumps/TOSHIBA_AL15SEB18EQY.json
|
||
|
create mode 100644 tests/smart_dumps/TOSHIBA_KPM5XMUG400G.json
|
||
|
create mode 100644 tests/smart_dumps/TOSHIBA_THNSNH128GBST.bin
|
||
|
create mode 100644 tests/smart_dumps/TOSHIBA_THNSNH128GBST.json
|
||
|
create mode 100644 tests/smart_dumps/WD4001FYYG-01SL3.json
|
||
|
create mode 100644 tests/smart_dumps/WDC_WD10EFRX-68PJCN0.json
|
||
|
create mode 100644 tests/smart_dumps/WDC_WD20EARS-00MVWB0.bin
|
||
|
create mode 100644 tests/smart_test.py
|
||
|
create mode 100644 tests/smartmontools_test.py
|
||
|
|
||
|
diff --git a/Makefile.am b/Makefile.am
|
||
|
index 6d378e6e..75ecf7f6 100644
|
||
|
--- a/Makefile.am
|
||
|
+++ b/Makefile.am
|
||
|
@@ -44,6 +44,14 @@ if !WITH_NVME
|
||
|
DISTCHECK_CONFIGURE_FLAGS += --without-nvme
|
||
|
endif
|
||
|
|
||
|
+if !WITH_SMART
|
||
|
+DISTCHECK_CONFIGURE_FLAGS += --without-smart
|
||
|
+endif
|
||
|
+
|
||
|
+if !WITH_SMARTMONTOOLS
|
||
|
+DISTCHECK_CONFIGURE_FLAGS += --without-smartmontools
|
||
|
+endif
|
||
|
+
|
||
|
if !WITH_SWAP
|
||
|
DISTCHECK_CONFIGURE_FLAGS += --without-swap
|
||
|
endif
|
||
|
@@ -68,7 +76,7 @@ MAINTAINERCLEANFILES = Makefile.in aclocal.m4 config.guess config.sub \
|
||
|
configure depcomp install-sh ltmain.sh missing py-compile compile ar-lib \
|
||
|
m4/*.m4
|
||
|
|
||
|
-LIBDIRS = src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/plugins/nvme/.libs:src/lib/.libs
|
||
|
+LIBDIRS = src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/plugins/nvme/.libs:src/plugins/smart/.libs:src/lib/.libs
|
||
|
GIDIR = src/lib
|
||
|
|
||
|
if WITH_PYTHON3
|
||
|
@@ -95,6 +103,7 @@ PLUGINS = btrfs \
|
||
|
nvme \
|
||
|
part \
|
||
|
s390 \
|
||
|
+ smart \
|
||
|
swap
|
||
|
|
||
|
ALT_PLUGINS = lvm-dbus
|
||
|
diff --git a/configure.ac b/configure.ac
|
||
|
index cc875bb4..975681f7 100644
|
||
|
--- a/configure.ac
|
||
|
+++ b/configure.ac
|
||
|
@@ -22,6 +22,7 @@ AC_CONFIG_FILES([Makefile src/Makefile \
|
||
|
src/plugins/Makefile \
|
||
|
src/plugins/fs/Makefile \
|
||
|
src/plugins/nvme/Makefile \
|
||
|
+ src/plugins/smart/Makefile \
|
||
|
src/utils/Makefile \
|
||
|
src/utils/blockdev-utils.pc \
|
||
|
src/lib/Makefile \
|
||
|
@@ -153,6 +154,28 @@ AS_IF([test "x$with_nvme" != "xno"],
|
||
|
[AC_DEFINE([WITH_BD_NVME], [], [Define if nvme is supported]) AC_SUBST([WITH_NVME], [1])],
|
||
|
[])
|
||
|
|
||
|
+AC_ARG_WITH([smart],
|
||
|
+ AS_HELP_STRING([--with-smart], [support ATA S.M.A.R.T. via libatasmart @<:@default=yes@:>@]),
|
||
|
+ [],
|
||
|
+ [with_smart=yes])
|
||
|
+
|
||
|
+AC_SUBST([WITH_SMART], [0])
|
||
|
+AM_CONDITIONAL(WITH_SMART, test "x$with_smart" != "xno")
|
||
|
+AS_IF([test "x$with_smart" != "xno"],
|
||
|
+ [AC_SUBST([WITH_SMART], [1])],
|
||
|
+ [])
|
||
|
+
|
||
|
+AC_ARG_WITH([smartmontools],
|
||
|
+ AS_HELP_STRING([--with-smartmontools], [support ATA/SCSI S.M.A.R.T. via smartmontools @<:@default=yes@:>@]),
|
||
|
+ [],
|
||
|
+ [with_smartmontools=yes])
|
||
|
+
|
||
|
+AC_SUBST([WITH_SMARTMONTOOLS], [0])
|
||
|
+AM_CONDITIONAL(WITH_SMARTMONTOOLS, test "x$with_smartmontools" != "xno")
|
||
|
+AS_IF([test "x$with_smartmontools" != "xno"],
|
||
|
+ [AC_SUBST([WITH_SMARTMONTOOLS], [1])],
|
||
|
+ [])
|
||
|
+
|
||
|
LIBBLOCKDEV_PLUGIN([BTRFS], [btrfs])
|
||
|
LIBBLOCKDEV_PLUGIN([CRYPTO], [crypto])
|
||
|
LIBBLOCKDEV_PLUGIN([DM], [dm])
|
||
|
@@ -166,6 +189,7 @@ LIBBLOCKDEV_PLUGIN([PART], [part])
|
||
|
LIBBLOCKDEV_PLUGIN([FS], [fs])
|
||
|
LIBBLOCKDEV_PLUGIN([NVDIMM], [nvdimm])
|
||
|
LIBBLOCKDEV_PLUGIN([NVME], [nvme])
|
||
|
+LIBBLOCKDEV_PLUGIN([SMART], [smart])
|
||
|
|
||
|
dnl these packages/modules are always needed
|
||
|
LIBBLOCKDEV_PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.42.2])
|
||
|
@@ -252,6 +276,63 @@ AS_IF([test "x$with_nvme" != "xno"],
|
||
|
],
|
||
|
[])
|
||
|
|
||
|
+AS_IF([test "x$with_smart" != "xno"],
|
||
|
+ [LIBBLOCKDEV_PKG_CHECK_MODULES([SMART], [libatasmart >= 0.17])
|
||
|
+ AC_DEFINE([HAVE_SMART])
|
||
|
+ drivedb_path="$datarootdir/smartmontools"
|
||
|
+ AC_ARG_WITH([drivedb],
|
||
|
+ [AS_HELP_STRING([--with-drivedb@<:@=PATH@:>@],
|
||
|
+ [support for smartmontools drivedb.h (default=$datarootdir/smartmontools)])],
|
||
|
+ [], [with_drivedb=$drivedb_path])
|
||
|
+ AX_RECURSIVE_EVAL([$with_drivedb], [drivedb_path])
|
||
|
+ case $drivedb_path in
|
||
|
+ yes)
|
||
|
+ ;;
|
||
|
+ /*)
|
||
|
+ if @<:@ -z ${drivedb_path##*drivedb.h} @:>@; then
|
||
|
+ drivedb_path=`dirname $drivedb_path`
|
||
|
+ fi
|
||
|
+ ;;
|
||
|
+ no|'')
|
||
|
+ ;;
|
||
|
+ *)
|
||
|
+ AC_MSG_RESULT([$drivedb_path])
|
||
|
+ AC_MSG_ERROR([The drivedb must be an absolute path.])
|
||
|
+ ;;
|
||
|
+ esac
|
||
|
+ ],
|
||
|
+ [])
|
||
|
+
|
||
|
+AS_IF([test "x$with_smart" != "xno"],
|
||
|
+ [SAVED_CFLAGS=$CFLAGS
|
||
|
+ CFLAGS+=" -I$drivedb_path"
|
||
|
+ AC_MSG_CHECKING([for drivedb.h presence])
|
||
|
+ AC_COMPILE_IFELSE(
|
||
|
+ [AC_LANG_PROGRAM([
|
||
|
+struct drive_settings {
|
||
|
+ const char* modelfamily;
|
||
|
+ const char* modelregexp;
|
||
|
+ const char* firmwareregexp;
|
||
|
+ const char* warningmsg;
|
||
|
+ const char* presets;
|
||
|
+};
|
||
|
+static const struct drive_settings builtin_knowndrives@<:@@:>@ = {
|
||
|
+#include <drivedb.h>
|
||
|
+};
|
||
|
+])],
|
||
|
+ [AC_MSG_RESULT([yes])
|
||
|
+ AC_DEFINE([HAVE_DRIVEDB_H])
|
||
|
+ AC_SUBST([DRIVEDB_H_CFLAGS], [-I$drivedb_path])],
|
||
|
+ [AC_MSG_RESULT([no])])
|
||
|
+ CFLAGS=$SAVED_CFLAGS
|
||
|
+ ],
|
||
|
+ [])
|
||
|
+
|
||
|
+AS_IF([test "x$with_smartmontools" != "xno"],
|
||
|
+ [LIBBLOCKDEV_PKG_CHECK_MODULES([JSON_GLIB], [json-glib-1.0])
|
||
|
+ AC_DEFINE([HAVE_SMARTMONTOOLS])],
|
||
|
+ [])
|
||
|
+
|
||
|
AC_SUBST([MAJOR_VER], [3])
|
||
|
|
||
|
CFLAGS="$CFLAGS -std=gnu99"
|
||
|
@@ -277,38 +358,40 @@ echo "
|
||
|
libblockdev $VERSION
|
||
|
====================
|
||
|
|
||
|
- prefix: ${prefix}
|
||
|
- libdir: ${libdir}
|
||
|
- libexecdir: ${libexecdir}
|
||
|
- bindir: ${bindir}
|
||
|
- sbindir: ${sbindir}
|
||
|
- datadir: ${datadir}
|
||
|
- sysconfdir: ${sysconfdir}
|
||
|
- localstatedir: ${localstatedir}
|
||
|
- docdir: ${docdir}
|
||
|
-
|
||
|
- compiler: ${CC}
|
||
|
- cflags: ${CFLAGS}
|
||
|
- cppflags: ${CPPFLAGS}
|
||
|
- ldflags: ${LDFLAGS}
|
||
|
-
|
||
|
- BTRFS plugin: ${with_btrfs}
|
||
|
- Crypto plugin: ${with_crypto}
|
||
|
- escrow support: ${with_escrow}
|
||
|
- DM plugin: ${with_dm}
|
||
|
- FS plugin: ${with_fs}
|
||
|
- Loop plugin: ${with_loop}
|
||
|
- LVM plugin: ${with_lvm}
|
||
|
- LVM DBus plugin: ${with_lvm_dbus}
|
||
|
- MDRAID plugin: ${with_mdraid}
|
||
|
- MPath plugin ${with_mpath}
|
||
|
- NVDIMM plugin (deprecated): ${with_nvdimm}
|
||
|
- NVMe plugin: ${with_nvme}
|
||
|
- Part plugin: ${with_part}
|
||
|
- S390 plugin: ${s390_info}
|
||
|
- Swap plugin: ${with_swap}
|
||
|
-
|
||
|
- GObject introspection: ${found_introspection}
|
||
|
- Python 3 bindings: ${python3_info}
|
||
|
- tools: ${with_tools}
|
||
|
+ prefix: ${prefix}
|
||
|
+ libdir: ${libdir}
|
||
|
+ libexecdir: ${libexecdir}
|
||
|
+ bindir: ${bindir}
|
||
|
+ sbindir: ${sbindir}
|
||
|
+ datadir: ${datadir}
|
||
|
+ sysconfdir: ${sysconfdir}
|
||
|
+ localstatedir: ${localstatedir}
|
||
|
+ docdir: ${docdir}
|
||
|
+
|
||
|
+ compiler: ${CC}
|
||
|
+ cflags: ${CFLAGS}
|
||
|
+ cppflags: ${CPPFLAGS}
|
||
|
+ ldflags: ${LDFLAGS}
|
||
|
+
|
||
|
+ BTRFS plugin: ${with_btrfs}
|
||
|
+ Crypto plugin: ${with_crypto}
|
||
|
+ escrow support: ${with_escrow}
|
||
|
+ DM plugin: ${with_dm}
|
||
|
+ FS plugin: ${with_fs}
|
||
|
+ Loop plugin: ${with_loop}
|
||
|
+ LVM plugin: ${with_lvm}
|
||
|
+ LVM DBus plugin: ${with_lvm_dbus}
|
||
|
+ MDRAID plugin: ${with_mdraid}
|
||
|
+ MPath plugin ${with_mpath}
|
||
|
+ NVDIMM plugin (deprecated): ${with_nvdimm}
|
||
|
+ NVMe plugin: ${with_nvme}
|
||
|
+ Part plugin: ${with_part}
|
||
|
+ S390 plugin: ${s390_info}
|
||
|
+ SMART plugin (libatasmart): ${with_smart}
|
||
|
+ SMART plugin (smartmontools): ${with_smartmontools}
|
||
|
+ Swap plugin: ${with_swap}
|
||
|
+
|
||
|
+ GObject introspection: ${found_introspection}
|
||
|
+ Python 3 bindings: ${python3_info}
|
||
|
+ tools: ${with_tools}
|
||
|
"
|
||
|
diff --git a/data/conf.d/00-default.cfg b/data/conf.d/00-default.cfg
|
||
|
index 95f8d7e0..495cec09 100644
|
||
|
--- a/data/conf.d/00-default.cfg
|
||
|
+++ b/data/conf.d/00-default.cfg
|
||
|
@@ -45,6 +45,9 @@ sonames=libbd_nvme.so.3
|
||
|
[part]
|
||
|
sonames=libbd_part.so.3
|
||
|
|
||
|
+[smart]
|
||
|
+sonames=libbd_smart.so.3;libbd_smartmontools.so.3
|
||
|
+
|
||
|
[swap]
|
||
|
sonames=libbd_swap.so.3
|
||
|
|
||
|
diff --git a/dist/libblockdev.spec.in b/dist/libblockdev.spec.in
|
||
|
index e8703411..8b04905e 100644
|
||
|
--- a/dist/libblockdev.spec.in
|
||
|
+++ b/dist/libblockdev.spec.in
|
||
|
@@ -16,6 +16,8 @@
|
||
|
%define with_escrow @WITH_ESCROW@
|
||
|
%define with_tools @WITH_TOOLS@
|
||
|
%define with_nvme @WITH_NVME@
|
||
|
+%define with_smart @WITH_SMART@
|
||
|
+%define with_smartmontools @WITH_SMARTMONTOOLS@
|
||
|
|
||
|
# btrfs is not available on RHEL > 7
|
||
|
%if 0%{?rhel} > 7 || %{with_btrfs} == 0
|
||
|
@@ -73,8 +75,14 @@
|
||
|
%if %{with_nvme} != 1
|
||
|
%define nvme_copts --without-nvme
|
||
|
%endif
|
||
|
+%if %{with_smart} != 1
|
||
|
+%define smart_copts --without-smart
|
||
|
+%endif
|
||
|
+%if %{with_smartmontools} != 1
|
||
|
+%define smartmontools_copts --without-smartmontools
|
||
|
+%endif
|
||
|
|
||
|
-%define configure_opts %{?python3_copts} %{?lvm_dbus_copts} %{?btrfs_copts} %{?crypto_copts} %{?dm_copts} %{?loop_copts} %{?lvm_copts} %{?lvm_dbus_copts} %{?mdraid_copts} %{?mpath_copts} %{?swap_copts} %{?part_copts} %{?fs_copts} %{?nvdimm_copts} %{?tools_copts} %{?gi_copts} %{?nvme_copts}
|
||
|
+%define configure_opts %{?python3_copts} %{?lvm_dbus_copts} %{?btrfs_copts} %{?crypto_copts} %{?dm_copts} %{?loop_copts} %{?lvm_copts} %{?lvm_dbus_copts} %{?mdraid_copts} %{?mpath_copts} %{?swap_copts} %{?part_copts} %{?fs_copts} %{?nvdimm_copts} %{?tools_copts} %{?gi_copts} %{?nvme_copts} %{?smart_copts} %{?smartmontools_copts}
|
||
|
|
||
|
Name: libblockdev
|
||
|
Version: 3.1.0
|
||
|
@@ -449,6 +457,53 @@ with the libblockdev-part plugin/library.
|
||
|
%endif
|
||
|
|
||
|
|
||
|
+%if %{with_smart}
|
||
|
+%package smart
|
||
|
+BuildRequires: libatasmart-devel >= 0.17
|
||
|
+Summary: The smart plugin for the libblockdev library
|
||
|
+Requires: %{name}-utils%{?_isa} = %{version}-%{release}
|
||
|
+
|
||
|
+%description smart
|
||
|
+The libblockdev library plugin (and in the same time a standalone library)
|
||
|
+providing S.M.A.R.T. monitoring and testing functionality, based
|
||
|
+on libatasmart.
|
||
|
+
|
||
|
+%package smart-devel
|
||
|
+Summary: Development files for the libblockdev-smart plugin/library
|
||
|
+Requires: %{name}-smart%{?_isa} = %{version}-%{release}
|
||
|
+Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release}
|
||
|
+Requires: glib2-devel
|
||
|
+
|
||
|
+%description smart-devel
|
||
|
+This package contains header files and pkg-config files needed for development
|
||
|
+with the libblockdev-smart plugin/library.
|
||
|
+%endif
|
||
|
+
|
||
|
+
|
||
|
+%if %{with_smartmontools}
|
||
|
+%package smartmontools
|
||
|
+BuildRequires: json-glib-devel
|
||
|
+Summary: The smartmontools plugin for the libblockdev library
|
||
|
+Requires: %{name}-utils%{?_isa} = %{version}-%{release}
|
||
|
+Requires: smartmontools >= 7.0
|
||
|
+
|
||
|
+%description smartmontools
|
||
|
+The libblockdev library plugin (and in the same time a standalone library)
|
||
|
+providing S.M.A.R.T. monitoring and testing functionality, based
|
||
|
+on smartmontools.
|
||
|
+
|
||
|
+%package smartmontools-devel
|
||
|
+Summary: Development files for the libblockdev-smart plugin/library
|
||
|
+Requires: %{name}-smartmontools%{?_isa} = %{version}-%{release}
|
||
|
+Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release}
|
||
|
+Requires: glib2-devel
|
||
|
+
|
||
|
+%description smartmontools-devel
|
||
|
+This package contains header files and pkg-config files needed for development
|
||
|
+with the libblockdev-smart plugin/library.
|
||
|
+%endif
|
||
|
+
|
||
|
+
|
||
|
%if %{with_swap}
|
||
|
%package swap
|
||
|
BuildRequires: libblkid-devel
|
||
|
@@ -556,6 +611,14 @@ Requires: %{name}-nvme%{?_isa} = %{version}-%{release}
|
||
|
Requires: %{name}-part%{?_isa} = %{version}-%{release}
|
||
|
%endif
|
||
|
|
||
|
+%if %{with_smart}
|
||
|
+Requires: %{name}-smart%{?_isa} = %{version}-%{release}
|
||
|
+%endif
|
||
|
+
|
||
|
+%if %{with_smartmontools}
|
||
|
+Requires: %{name}-smartmontools%{?_isa} = %{version}-%{release}
|
||
|
+%endif
|
||
|
+
|
||
|
%if %{with_swap}
|
||
|
Requires: %{name}-swap%{?_isa} = %{version}-%{release}
|
||
|
%endif
|
||
|
@@ -632,6 +695,14 @@ find %{buildroot} -type f -name "*.la" | xargs %{__rm}
|
||
|
%ldconfig_scriptlets part
|
||
|
%endif
|
||
|
|
||
|
+%if %{with_smart}
|
||
|
+%ldconfig_scriptlets smart
|
||
|
+%endif
|
||
|
+
|
||
|
+%if %{with_smartmontools}
|
||
|
+%ldconfig_scriptlets smartmontools
|
||
|
+%endif
|
||
|
+
|
||
|
%if %{with_swap}
|
||
|
%ldconfig_scriptlets swap
|
||
|
%endif
|
||
|
@@ -823,6 +894,28 @@ find %{buildroot} -type f -name "*.la" | xargs %{__rm}
|
||
|
%endif
|
||
|
|
||
|
|
||
|
+%if %{with_smart}
|
||
|
+%files smart
|
||
|
+%{_libdir}/libbd_smart.so.*
|
||
|
+
|
||
|
+%files smart-devel
|
||
|
+%{_libdir}/libbd_smart.so
|
||
|
+%dir %{_includedir}/blockdev
|
||
|
+%{_includedir}/blockdev/smart.h
|
||
|
+%endif
|
||
|
+
|
||
|
+
|
||
|
+%if %{with_smartmontools}
|
||
|
+%files smartmontools
|
||
|
+%{_libdir}/libbd_smartmontools.so.*
|
||
|
+
|
||
|
+%files smartmontools-devel
|
||
|
+%{_libdir}/libbd_smartmontools.so
|
||
|
+%dir %{_includedir}/blockdev
|
||
|
+%{_includedir}/blockdev/smart.h
|
||
|
+%endif
|
||
|
+
|
||
|
+
|
||
|
%if %{with_swap}
|
||
|
%files swap
|
||
|
%{_libdir}/libbd_swap.so.*
|
||
|
diff --git a/docs/libblockdev-docs.xml.in b/docs/libblockdev-docs.xml.in
|
||
|
index 1e67d0ae..c30d124c 100644
|
||
|
--- a/docs/libblockdev-docs.xml.in
|
||
|
+++ b/docs/libblockdev-docs.xml.in
|
||
|
@@ -39,6 +39,7 @@
|
||
|
<xi:include href="xml/nvme.xml"/>
|
||
|
<xi:include href="xml/plugins.xml"/>
|
||
|
<xi:include href="xml/part.xml"/>
|
||
|
+ <xi:include href="xml/smart.xml"/>
|
||
|
<xi:include href="xml/swap.xml"/>
|
||
|
<xi:include href="xml/s390.xml"/>
|
||
|
<xi:include href="xml/utils.xml"/>
|
||
|
diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
|
||
|
index c58d11a6..5d41c0f9 100644
|
||
|
--- a/docs/libblockdev-sections.txt
|
||
|
+++ b/docs/libblockdev-sections.txt
|
||
|
@@ -168,6 +168,7 @@ bd_utils_dev_utils_error_quark
|
||
|
bd_utils_exec_and_report_error
|
||
|
bd_utils_exec_and_report_status_error
|
||
|
bd_utils_exec_and_capture_output
|
||
|
+bd_utils_exec_and_capture_output_no_progress
|
||
|
bd_utils_exec_and_report_error_no_progress
|
||
|
bd_utils_exec_and_report_progress
|
||
|
bd_utils_exec_with_input
|
||
|
@@ -816,3 +817,41 @@ bd_nvme_disconnect
|
||
|
bd_nvme_disconnect_by_path
|
||
|
bd_nvme_find_ctrls_for_ns
|
||
|
</SECTION>
|
||
|
+
|
||
|
+<SECTION>
|
||
|
+<FILE>smart</FILE>
|
||
|
+bd_smart_check_deps
|
||
|
+bd_smart_close
|
||
|
+bd_smart_init
|
||
|
+bd_smart_error_quark
|
||
|
+BD_SMART_ERROR
|
||
|
+BDSmartError
|
||
|
+BDSmartTech
|
||
|
+BDSmartTechMode
|
||
|
+bd_smart_is_tech_avail
|
||
|
+BD_SMART_TYPE_ATA
|
||
|
+BD_SMART_TYPE_ATA_ATTRIBUTE
|
||
|
+BDSmartATAAttribute
|
||
|
+BDSmartATAAttributeUnit
|
||
|
+BDSmartATAAttributeFlag
|
||
|
+BDSmartATACapabilities
|
||
|
+BDSmartATAOfflineDataCollectionCapabilities
|
||
|
+BDSmartATAOfflineDataCollectionStatus
|
||
|
+BDSmartATASelfTestStatus
|
||
|
+BDSmartATA
|
||
|
+bd_smart_ata_get_info
|
||
|
+bd_smart_ata_get_info_from_data
|
||
|
+bd_smart_ata_copy
|
||
|
+bd_smart_ata_free
|
||
|
+bd_smart_ata_attribute_copy
|
||
|
+bd_smart_ata_attribute_free
|
||
|
+BDSmartSCSIInformationalException
|
||
|
+BDSmartSCSIBackgroundScanStatus
|
||
|
+BDSmartSCSI
|
||
|
+bd_smart_scsi_get_info
|
||
|
+bd_smart_scsi_free
|
||
|
+bd_smart_scsi_copy
|
||
|
+bd_smart_set_enabled
|
||
|
+BDSmartSelfTestOp
|
||
|
+bd_smart_device_self_test
|
||
|
+</SECTION>
|
||
|
diff --git a/include/blockdev/Makefile.am b/include/blockdev/Makefile.am
|
||
|
index e6246748..a26bf60e 100644
|
||
|
--- a/include/blockdev/Makefile.am
|
||
|
+++ b/include/blockdev/Makefile.am
|
||
|
@@ -1,6 +1,7 @@
|
||
|
all-local:
|
||
|
for header in ${srcdir}/../../src/plugins/*.h; do ln -sf $${header} ./; done
|
||
|
for header in ${srcdir}/../../src/plugins/nvme/nvme.h; do ln -sf $${header} ./; done
|
||
|
+ for header in ${srcdir}/../../src/plugins/smart/smart.h; do ln -sf $${header} ./; done
|
||
|
for header in ${srcdir}/../../src/utils/*.h; do ln -sf $${header} ./; done
|
||
|
for header in ${srcdir}/../../src/lib/*.h; do ln -sf $${header} ./; done
|
||
|
mkdir -p fs;
|
||
|
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
|
||
|
index b8637d70..b75042c0 100644
|
||
|
--- a/src/lib/Makefile.am
|
||
|
+++ b/src/lib/Makefile.am
|
||
|
@@ -31,7 +31,8 @@ GIHEADERS = ${builddir}/plugin_apis/mdraid.h \
|
||
|
${builddir}/plugin_apis/part.h \
|
||
|
${builddir}/plugin_apis/fs.h \
|
||
|
${builddir}/plugin_apis/nvdimm.h \
|
||
|
- ${builddir}/plugin_apis/nvme.h
|
||
|
+ ${builddir}/plugin_apis/nvme.h \
|
||
|
+ ${builddir}/plugin_apis/smart.h
|
||
|
|
||
|
GIHEADERS += $(wildcard ${srcdir}/../utils/*.[ch])
|
||
|
GIHEADERS += blockdev.c blockdev.h plugins.c plugins.h
|
||
|
diff --git a/src/lib/blockdev.c.in b/src/lib/blockdev.c.in
|
||
|
index aeecc03d..774e4492 100644
|
||
|
--- a/src/lib/blockdev.c.in
|
||
|
+++ b/src/lib/blockdev.c.in
|
||
|
@@ -27,6 +27,8 @@
|
||
|
#include "plugin_apis/nvdimm.c"
|
||
|
#include "plugin_apis/nvme.h"
|
||
|
#include "plugin_apis/nvme.c"
|
||
|
+#include "plugin_apis/smart.h"
|
||
|
+#include "plugin_apis/smart.c"
|
||
|
|
||
|
#if defined(__s390__) || defined(__s390x__)
|
||
|
#include "plugin_apis/s390.h"
|
||
|
@@ -61,7 +63,12 @@ static gchar * default_plugin_so[BD_PLUGIN_UNDEF] = {
|
||
|
"libbd_dm.so.@MAJOR_VER@", "libbd_mdraid.so.@MAJOR_VER@",
|
||
|
"libbd_s390.so.@MAJOR_VER@", "libbd_part.so.@MAJOR_VER@",
|
||
|
"libbd_fs.so.@MAJOR_VER@", "libbd_nvdimm.so.@MAJOR_VER@",
|
||
|
- "libbd_nvme.so.@MAJOR_VER@"
|
||
|
+ "libbd_nvme.so.@MAJOR_VER@",
|
||
|
+#if defined(HAVE_SMART) || !defined(HAVE_SMARTMONTOOLS)
|
||
|
+ "libbd_smart.so.@MAJOR_VER@",
|
||
|
+#else
|
||
|
+ "libbd_smartmontools.so.@MAJOR_VER@",
|
||
|
+#endif
|
||
|
};
|
||
|
static BDPluginStatus plugins[BD_PLUGIN_UNDEF] = {
|
||
|
{{BD_PLUGIN_LVM, NULL}, NULL},
|
||
|
@@ -77,9 +84,10 @@ static BDPluginStatus plugins[BD_PLUGIN_UNDEF] = {
|
||
|
{{BD_PLUGIN_FS, NULL}, NULL},
|
||
|
{{BD_PLUGIN_NVDIMM, NULL}, NULL},
|
||
|
{{BD_PLUGIN_NVME, NULL}, NULL},
|
||
|
+ {{BD_PLUGIN_SMART, NULL}, NULL},
|
||
|
};
|
||
|
static gchar* plugin_names[BD_PLUGIN_UNDEF] = {
|
||
|
- "lvm", "btrfs", "swap", "loop", "crypto", "mpath", "dm", "mdraid", "s390", "part", "fs", "nvdimm", "nvme"
|
||
|
+ "lvm", "btrfs", "swap", "loop", "crypto", "mpath", "dm", "mdraid", "s390", "part", "fs", "nvdimm", "nvme", "smart"
|
||
|
};
|
||
|
|
||
|
static void set_plugin_so_name (BDPlugin name, const gchar *so_name) {
|
||
|
@@ -234,6 +242,10 @@ static void unload_plugins (void) {
|
||
|
if (plugins[BD_PLUGIN_NVME].handle && !unload_nvme (plugins[BD_PLUGIN_NVME].handle))
|
||
|
bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to close the nvme plugin");
|
||
|
plugins[BD_PLUGIN_NVME].handle = NULL;
|
||
|
+
|
||
|
+ if (plugins[BD_PLUGIN_SMART].handle && !unload_smart (plugins[BD_PLUGIN_SMART].handle))
|
||
|
+ bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to close the smart plugin");
|
||
|
+ plugins[BD_PLUGIN_SMART].handle = NULL;
|
||
|
}
|
||
|
|
||
|
static void load_plugin_from_sonames (BDPlugin plugin, LoadFunc load_fn, void **handle, GSList *sonames) {
|
||
|
@@ -274,6 +286,8 @@ static void do_load (GSList **plugins_sonames) {
|
||
|
load_plugin_from_sonames (BD_PLUGIN_NVDIMM, load_nvdimm_from_plugin, &(plugins[BD_PLUGIN_NVDIMM].handle), plugins_sonames[BD_PLUGIN_NVDIMM]);
|
||
|
if (!plugins[BD_PLUGIN_NVME].handle && plugins_sonames[BD_PLUGIN_NVME])
|
||
|
load_plugin_from_sonames (BD_PLUGIN_NVME, load_nvme_from_plugin, &(plugins[BD_PLUGIN_NVME].handle), plugins_sonames[BD_PLUGIN_NVME]);
|
||
|
+ if (!plugins[BD_PLUGIN_SMART].handle && plugins_sonames[BD_PLUGIN_SMART])
|
||
|
+ load_plugin_from_sonames (BD_PLUGIN_SMART, load_smart_from_plugin, &(plugins[BD_PLUGIN_SMART].handle), plugins_sonames[BD_PLUGIN_SMART]);
|
||
|
}
|
||
|
|
||
|
static gboolean load_plugins (BDPluginSpec **require_plugins, gboolean reload, guint64 *num_loaded) {
|
||
|
@@ -283,7 +297,7 @@ static gboolean load_plugins (BDPluginSpec **require_plugins, gboolean reload, g
|
||
|
GSequence *config_files = NULL;
|
||
|
GSList *plugins_sonames[BD_PLUGIN_UNDEF] = {NULL, NULL, NULL, NULL, NULL,
|
||
|
NULL, NULL, NULL, NULL, NULL,
|
||
|
- NULL};
|
||
|
+ NULL, NULL};
|
||
|
BDPlugin plugin_name = BD_PLUGIN_UNDEF;
|
||
|
guint64 required_plugins_mask = 0;
|
||
|
|
||
|
diff --git a/src/lib/plugin_apis/smart.api b/src/lib/plugin_apis/smart.api
|
||
|
new file mode 100644
|
||
|
index 00000000..bd84da15
|
||
|
--- /dev/null
|
||
|
+++ b/src/lib/plugin_apis/smart.api
|
||
|
@@ -0,0 +1,678 @@
|
||
|
+#include <glib.h>
|
||
|
+#include <glib-object.h>
|
||
|
+
|
||
|
+#ifndef BD_SMART_API
|
||
|
+#define BD_SMART_API
|
||
|
+
|
||
|
+GQuark bd_smart_error_quark (void) {
|
||
|
+ return g_quark_from_static_string ("g-bd-smart-error-quark");
|
||
|
+}
|
||
|
+
|
||
|
+#define BD_SMART_ERROR bd_smart_error_quark ()
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartError:
|
||
|
+ * @BD_SMART_ERROR_TECH_UNAVAIL: SMART support not available.
|
||
|
+ * @BD_SMART_ERROR_FAILED: General error.
|
||
|
+ * @BD_SMART_ERROR_INVALID_ARGUMENT: Invalid argument.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ERROR_TECH_UNAVAIL,
|
||
|
+ BD_SMART_ERROR_FAILED,
|
||
|
+ BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+} BDSmartError;
|
||
|
+
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_TECH_ATA = 0,
|
||
|
+ BD_SMART_TECH_SCSI = 1,
|
||
|
+} BDSmartTech;
|
||
|
+
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_TECH_MODE_INFO = 1 << 0,
|
||
|
+ BD_SMART_TECH_MODE_SELFTEST = 1 << 1,
|
||
|
+} BDSmartTechMode;
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_is_tech_avail:
|
||
|
+ * @tech: the queried tech
|
||
|
+ * @mode: a bit mask of queried modes of operation (#BDSmartTechMode) for @tech
|
||
|
+ * @error: (out) (nullable): place to store error (details about why the @tech-@mode combination is not available)
|
||
|
+ *
|
||
|
+ * Returns: whether the @tech-@mode combination is available -- supported by the
|
||
|
+ * plugin implementation and having all the runtime dependencies available
|
||
|
+ */
|
||
|
+gboolean bd_smart_is_tech_avail (BDSmartTechMode tech, G_GNUC_UNUSED guint64 mode, GError **error);
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartATAOfflineDataCollectionStatus:
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED: Offline data collection activity was never started.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR: Offline data collection activity was completed without error. Indicates a passed test.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS: Offline data collection activity is in progress.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR: Offline data collection activity was suspended by an interrupting command from host.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR: Offline data collection activity was aborted by an interrupting command from host.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR: Offline data collection activity was aborted by the device with a fatal error. Indicates a failed test.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC: Offline data collection activity is in a Vendor Specific state.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED: Offline data collection activity is in a Reserved state.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED = 0x00,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR = 0x02,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS = 0x03,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR = 0x04,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR = 0x05,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR = 0x06,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC = 0x40,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED = 0x3F,
|
||
|
+} BDSmartATAOfflineDataCollectionStatus;
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartATAOfflineDataCollectionCapabilities:
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_NOT_SUPPORTED: Offline data collection not supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_EXEC_OFFLINE_IMMEDIATE: Execute Offline Immediate function supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_ABORT: Abort Offline collection upon new command.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_SURFACE_SCAN: Offline surface scan supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELF_TEST: Self-test supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_CONVEYANCE_SELF_TEST: Conveyance Self-test supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELECTIVE_SELF_TEST: Selective Self-test supported.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_NOT_SUPPORTED = 0x00,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_EXEC_OFFLINE_IMMEDIATE = 0x01,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_ABORT = 0x04,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_SURFACE_SCAN = 0x08,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELF_TEST = 0x10,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_CONVEYANCE_SELF_TEST = 0x20,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELECTIVE_SELF_TEST = 0x40,
|
||
|
+} BDSmartATAOfflineDataCollectionCapabilities;
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartATASelfTestStatus:
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR: The previous self-test routine completed without error or no self-test has ever been run.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST: The self-test routine was aborted by the host.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET: The self-test routine was interrupted by the host with a hard or soft reset.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_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.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN: The previous self-test completed having a test element that failed and the test element that failed is not known.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL: The previous self-test completed having the electrical element of the test failed.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO: The previous self-test completed having the servo (and/or seek) element of the test failed.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ: The previous self-test completed having the read element of the test failed.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING: The previous self-test completed having a test element that failed and the device is suspected of having handling damage.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS: Self-test routine in progress.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR = 0x00,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST = 0x01,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET = 0x02,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_FATAL = 0x03,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN = 0x04,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL = 0x05,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO = 0x06,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ = 0x07,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING = 0x08,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS = 0x0F,
|
||
|
+} BDSmartATASelfTestStatus;
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartATACapabilities:
|
||
|
+ * @BD_SMART_ATA_CAP_ATTRIBUTE_AUTOSAVE: Saves SMART data before entering power-saving mode.
|
||
|
+ * @BD_SMART_ATA_CAP_AUTOSAVE_TIMER: Supports SMART auto save timer.
|
||
|
+ * @BD_SMART_ATA_CAP_ERROR_LOGGING: Error logging supported.
|
||
|
+ * @BD_SMART_ATA_CAP_GP_LOGGING: General Purpose Logging supported.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_CAP_ATTRIBUTE_AUTOSAVE = 1 << 0,
|
||
|
+ BD_SMART_ATA_CAP_AUTOSAVE_TIMER = 1 << 1,
|
||
|
+ BD_SMART_ATA_CAP_ERROR_LOGGING = 1 << 2,
|
||
|
+ BD_SMART_ATA_CAP_GP_LOGGING = 1 << 3,
|
||
|
+} BDSmartATACapabilities;
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartATAAttributeUnit:
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN: Unknown.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_NONE: Dimensionless value.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS: Milliseconds.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS: Sectors.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN: Millikelvin.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT: Percentage with 3 decimal points.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT: Integer percentage.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_MB: Megabytes.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_NONE,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_MB,
|
||
|
+} BDSmartATAAttributeUnit;
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartATAAttributeFlag:
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_PREFAILURE: Pre-failure/advisory bit: If the value of this bit equals zero, an attribute value less than or equal to its corresponding attribute threshold indicates an advisory condition where the usage or age of the device has exceeded its intended design life period. If the value of this bit equals one, an attribute value less than or equal to its corresponding attribute threshold indicates a prefailure condition where imminent loss of data is being predicted.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_ONLINE: On-line data collection bit: If the value of this bit equals zero, then the attribute value is updated only during off-line data collection activities. If the value of this bit equals one, then the attribute value is updated during normal operation of the device or during both normal operation and off-line testing.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_PERFORMANCE: Performance type bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_ERROR_RATE: Errorrate type bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_EVENT_COUNT: Eventcount bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_SELF_PRESERVING: Selfpereserving bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_OTHER: Reserved.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_PREFAILURE = 0x0001,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_ONLINE = 0x0002,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_PERFORMANCE = 0x0004,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_ERROR_RATE = 0x0008,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_EVENT_COUNT = 0x0010,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_SELF_PRESERVING = 0x0020,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_OTHER = 0xffc0,
|
||
|
+} BDSmartATAAttributeFlag;
|
||
|
+
|
||
|
+
|
||
|
+#define BD_SMART_TYPE_ATA_ATTRIBUTE (bd_smart_ata_attribute_get_type ())
|
||
|
+GType bd_smart_ata_attribute_get_type ();
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATAAttribute:
|
||
|
+ * @id: Attribute Identifier.
|
||
|
+ * @name: A free-form representation of the attribute name, implementation-dependent (e.g. libatasmart internal strings or smartmontools' drivedb.h names).
|
||
|
+ * @well_known_name: Translated well-known attribute name (in libatasmart style, e.g. 'raw-read-error-rate') or %NULL in case of unknown, untrusted or vendor-specific value.
|
||
|
+ * @value: The normalized value or -1 if unknown.
|
||
|
+ * @worst: The worst normalized value of -1 if unknown.
|
||
|
+ * @threshold: The threshold of a normalized value or -1 if unknown.
|
||
|
+ * @failed_past: Indicates a failure that happened in the past (the normalized worst value is below the threshold).
|
||
|
+ * @failing_now: Indicates a failure that is happening now (the normalized value is below the threshold).
|
||
|
+ * @value_raw: The raw value of the attribute.
|
||
|
+ * @flags: Bitmask of attribute flags. See #BDSmartATAAttributeFlag.
|
||
|
+ * @pretty_value: Numerical representation of the parsed raw value, presented in @pretty_value_unit units.
|
||
|
+ * @pretty_value_unit: The unit of the parsed raw value.
|
||
|
+ * @pretty_value_string: A free-form string representation of the raw value intended for user presentation or %NULL.
|
||
|
+ */
|
||
|
+typedef struct BDSmartATAAttribute {
|
||
|
+ guint8 id;
|
||
|
+ gchar *name;
|
||
|
+ gchar *well_known_name;
|
||
|
+ gint value;
|
||
|
+ gint worst;
|
||
|
+ gint threshold;
|
||
|
+ gboolean failed_past;
|
||
|
+ gboolean failing_now;
|
||
|
+ guint64 value_raw;
|
||
|
+ guint16 flags;
|
||
|
+ gint64 pretty_value;
|
||
|
+ BDSmartATAAttributeUnit pretty_value_unit;
|
||
|
+ gchar *pretty_value_string;
|
||
|
+} BDSmartATAAttribute;
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_attribute_free: (skip)
|
||
|
+ * @attr: (nullable): %BDSmartATAAttribute to free
|
||
|
+ *
|
||
|
+ * Frees @attr.
|
||
|
+ */
|
||
|
+void bd_smart_ata_attribute_free (BDSmartATAAttribute *attr) {
|
||
|
+ if (attr == NULL)
|
||
|
+ return;
|
||
|
+ g_free (attr->name);
|
||
|
+ g_free (attr->well_known_name);
|
||
|
+ g_free (attr->pretty_value_string);
|
||
|
+ g_free (attr);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_attribute_copy: (skip)
|
||
|
+ * @attr: (nullable): %BDSmartATAAttribute to copy
|
||
|
+ *
|
||
|
+ * Creates a new copy of @attr.
|
||
|
+ */
|
||
|
+BDSmartATAAttribute * bd_smart_ata_attribute_copy (BDSmartATAAttribute *attr) {
|
||
|
+ BDSmartATAAttribute *new_attr;
|
||
|
+
|
||
|
+ if (attr == NULL)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ new_attr = g_new0 (BDSmartATAAttribute, 1);
|
||
|
+ memcpy (new_attr, attr, sizeof (BDSmartATAAttribute));
|
||
|
+ new_attr->name = g_strdup (attr->name);
|
||
|
+ new_attr->well_known_name = g_strdup (attr->well_known_name);
|
||
|
+ new_attr->pretty_value_string = g_strdup (attr->pretty_value_string);
|
||
|
+
|
||
|
+ return new_attr;
|
||
|
+}
|
||
|
+
|
||
|
+GType bd_smart_ata_attribute_get_type () {
|
||
|
+ static GType type = 0;
|
||
|
+
|
||
|
+ if (G_UNLIKELY (type == 0)) {
|
||
|
+ type = g_boxed_type_register_static ("BDSmartATAAttribute",
|
||
|
+ (GBoxedCopyFunc) bd_smart_ata_attribute_copy,
|
||
|
+ (GBoxedFreeFunc) bd_smart_ata_attribute_free);
|
||
|
+ }
|
||
|
+ return type;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+#define BD_SMART_TYPE_ATA (bd_smart_ata_get_type ())
|
||
|
+GType bd_smart_ata_get_type ();
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATA:
|
||
|
+ * @smart_supported: Indicates that the device has SMART capability.
|
||
|
+ * @smart_enabled: Indicates that the SMART support is enabled.
|
||
|
+ * @overall_status_passed: %TRUE if the device SMART overall-health self-assessment test result has passed.
|
||
|
+ * @offline_data_collection_status: The offline data collection status. See #BDSmartATAOfflineDataCollectionStatus.
|
||
|
+ * @auto_offline_data_collection_enabled: %TRUE if Automatic Offline Data Collection is enabled. Only supported with the smartmontools plugin.
|
||
|
+ * @offline_data_collection_completion: Total time in seconds to complete Offline data collection.
|
||
|
+ * @offline_data_collection_capabilities: Bitmask of offline data collection capabilities, see #BDSmartATAOfflineDataCollectionCapabilities. Only supported with the smartmontools plugin.
|
||
|
+ * @self_test_status: Self-test execution status. See #BDSmartATASelfTestStatus.
|
||
|
+ * @self_test_percent_remaining: The percentage remaining of a running self-test.
|
||
|
+ * @self_test_polling_short: Short self-test routine recommended polling time in minutes or 0 if not supported.
|
||
|
+ * @self_test_polling_extended: Extended self-test routine recommended polling time in minutes or 0 if not supported.
|
||
|
+ * @self_test_polling_conveyance: Conveyance self-test routine recommended polling time in minutes or 0 if not supported.
|
||
|
+ * @smart_capabilities: Bitmask of device misc. SMART capabilities. See #BDSmartATACapabilities. Only supported with the smartmontools plugin.
|
||
|
+ * @attributes: (array zero-terminated=1): A list of reported SMART attributes.
|
||
|
+ * @power_on_time: The count of minutes in power-on state.
|
||
|
+ * @power_cycle_count: The count of full hard disk power on/off cycles.
|
||
|
+ * @temperature: The current drive temperature in Kelvin or 0 when temperature is not reported.
|
||
|
+ */
|
||
|
+typedef struct BDSmartATA {
|
||
|
+ gboolean smart_supported;
|
||
|
+ gboolean smart_enabled;
|
||
|
+ gboolean overall_status_passed;
|
||
|
+ BDSmartATAOfflineDataCollectionStatus offline_data_collection_status;
|
||
|
+ gboolean auto_offline_data_collection_enabled;
|
||
|
+ gint offline_data_collection_completion;
|
||
|
+ guint offline_data_collection_capabilities;
|
||
|
+ BDSmartATASelfTestStatus self_test_status;
|
||
|
+ gint self_test_percent_remaining;
|
||
|
+ gint self_test_polling_short;
|
||
|
+ gint self_test_polling_extended;
|
||
|
+ gint self_test_polling_conveyance;
|
||
|
+ guint smart_capabilities;
|
||
|
+ BDSmartATAAttribute **attributes;
|
||
|
+ guint power_on_time;
|
||
|
+ guint64 power_cycle_count;
|
||
|
+ guint temperature;
|
||
|
+} BDSmartATA;
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_free: (skip)
|
||
|
+ * @data: (nullable): %BDSmartATA to free
|
||
|
+ *
|
||
|
+ * Frees @data.
|
||
|
+ */
|
||
|
+void bd_smart_ata_free (BDSmartATA *data) {
|
||
|
+ BDSmartATAAttribute **attr;
|
||
|
+
|
||
|
+ if (data == NULL)
|
||
|
+ return;
|
||
|
+
|
||
|
+ for (attr = data->attributes; attr && *attr; attr++)
|
||
|
+ bd_smart_ata_attribute_free (*attr);
|
||
|
+ g_free (data->attributes);
|
||
|
+ g_free (data);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_copy: (skip)
|
||
|
+ * @data: (nullable): %BDSmartATA to copy
|
||
|
+ *
|
||
|
+ * Creates a new copy of @data.
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_copy (BDSmartATA *data) {
|
||
|
+ BDSmartATA *new_data;
|
||
|
+ BDSmartATAAttribute **attr;
|
||
|
+ GPtrArray *ptr_array;
|
||
|
+
|
||
|
+ if (data == NULL)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ new_data = g_new0 (BDSmartATA, 1);
|
||
|
+ memcpy (new_data, data, sizeof (BDSmartATA));
|
||
|
+
|
||
|
+ ptr_array = g_ptr_array_new ();
|
||
|
+ for (attr = data->attributes; attr && *attr; attr++)
|
||
|
+ g_ptr_array_add (ptr_array, bd_smart_ata_attribute_copy (*attr));
|
||
|
+ g_ptr_array_add (ptr_array, NULL);
|
||
|
+ new_data->attributes = (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);
|
||
|
+
|
||
|
+ return new_data;
|
||
|
+}
|
||
|
+
|
||
|
+GType bd_smart_ata_get_type () {
|
||
|
+ static GType type = 0;
|
||
|
+
|
||
|
+ if (G_UNLIKELY (type == 0)) {
|
||
|
+ type = g_boxed_type_register_static ("BDSmartATA",
|
||
|
+ (GBoxedCopyFunc) bd_smart_ata_copy,
|
||
|
+ (GBoxedFreeFunc) bd_smart_ata_free);
|
||
|
+ }
|
||
|
+ return type;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartSCSIInformationalException:
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NONE: No SCSI Informational Exception raised.
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ABORTED_COMMAND: Warning - aborted command [asc 0x0b, ascq 0x00].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_TEMPERATURE_EXCEEDED: Warning - specified temperature exceeded [asc 0x0b, ascq 0x01].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ENCLOSURE_DEGRADED: Warning - enclosure degraded [asc 0x0b, ascq 0x02].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SELFTEST_FAILED: Warning - background self-test failed [asc 0x0b, ascq 0x03].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_PRESCAN_MEDIUM_ERROR: Warning - background pre-scan detected medium error [asc 0x0b, ascq 0x04].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SCAN_MEDIUM_ERROR: Warning - background medium scan detected medium error [asc 0x0b, ascq 0x05].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_VOLATILE: Warning - non-volatile cache now volatile [asc 0x0b, ascq 0x06].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_DEGRADED_POWER: Warning - degraded power to non-volatile cache [asc 0x0b, ascq 0x07].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_POWER_LOSS_EXPECTED: Warning - power loss expected [asc 0x0b, ascq 0x08].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_STATISTICS_NOTIFICATION: Warning - device statistics notification active [asc 0x0b, ascq 0x09].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_TEMP: Warning - high critical temperature limit exceeded [asc 0x0b, ascq 0x0a].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_TEMP: Warning - low critical temperature limit exceeded [asc 0x0b, ascq 0x0b].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_TEMP: Warning - high operating temperature limit exceeded [asc 0x0b, ascq 0x0c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_TEMP: Warning - low operating temperature limit exceeded [asc 0x0b, ascq 0x0d].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_HUMIDITY: Warning - high critical humidity limit exceeded [asc 0x0b, ascq 0x0e].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_HUMIDITY: Warning - low critical humidity limit exceeded [asc 0x0b, ascq 0x0f].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_HUMIDITY: Warning - high operating humidity limit exceeded [asc 0x0b, ascq 0x10].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_HUMIDITY: Warning - low operating humidity limit exceeded [asc 0x0b, ascq 0x11].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SECURITY_RISK: Warning - microcode security at risk [asc 0x0b, ascq 0x12].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SIGNATURE_VALIDATION_FAILURE: Warning - microcode digital signature validation failure [asc 0x0b, ascq 0x13].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_PHYSICAL_ELEMENT_STATUS_CHANGE: Warning - physical element status change [asc 0x0b, ascq 0x14].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FAILURE_PREDICTION_THRESH: Failure prediction threshold exceeded [asc 0x5d, ascq 0x00, ascq 0xff].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_FAILURE_PREDICTION_THRESH: Media failure prediction threshold exceeded [asc 0x5d, ascq 0x01].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOGICAL_UNIT_FAILURE_PREDICTION_THRESH: Logical unit failure prediction threshold exceeded [asc 0x5d, ascq 0x02].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPARE_EXHAUSTION_PREDICTION_THRESH: Spare area exhaustion prediction threshold exceeded [asc 0x5d, ascq 0x03].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HARDWARE_IMPENDING_FAILURE: Hardware impending failure [asc 0x5d, ascq 0x10..0x1d].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_CONTROLLER_IMPENDING_FAILURE: Controller impending failure [asc 0x5d, ascq 0x20..0x2c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_DATA_CHANNEL_IMPENDING_FAILURE: Data channel impending failure [asc 0x5d, ascq 0x30..0x3c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SERVO_IMPENDING_FAILURE: Servo impending failure [asc 0x5d, ascq 0x40..0x4c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPINDLE_IMPENDING_FAILURE: Spindle impending failure [asc 0x5d, ascq 0x50..0x5c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FIRMWARE_IMPENDING_FAILURE: Firmware impending failure [asc 0x5d, ascq 0x60..0x6c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_ENDURANCE_LIMIT: Media impending failure endurance limit met [asc 0x5d, ascq 0x73].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED: Unspecified warning.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NONE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ABORTED_COMMAND,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_TEMPERATURE_EXCEEDED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ENCLOSURE_DEGRADED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SELFTEST_FAILED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_PRESCAN_MEDIUM_ERROR,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SCAN_MEDIUM_ERROR,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_VOLATILE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_DEGRADED_POWER,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_POWER_LOSS_EXPECTED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_STATISTICS_NOTIFICATION,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SECURITY_RISK,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SIGNATURE_VALIDATION_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_PHYSICAL_ELEMENT_STATUS_CHANGE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FAILURE_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_FAILURE_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOGICAL_UNIT_FAILURE_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPARE_EXHAUSTION_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HARDWARE_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_CONTROLLER_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_DATA_CHANNEL_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SERVO_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPINDLE_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FIRMWARE_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_ENDURANCE_LIMIT,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED,
|
||
|
+} BDSmartSCSIInformationalException;
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartSCSIBackgroundScanStatus:
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_NO_SCANS_ACTIVE: No scans active.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_SCAN_ACTIVE: Scan is active.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_PRESCAN_ACTIVE: Pre-scan is active.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_FATAL: Halted due to fatal error.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_PATTERN_VENDOR_SPECIFIC: Halted due to a vendor specific pattern of error.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_PLIST: Halted due to medium formatted without P-List.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_VENDOR_SPECIFIC: Halted - vendor specific cause.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_TEMPERATURE: Halted due to temperature out of range.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_BMS_TIMER: Waiting until BMS interval timer expires.
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_NO_SCANS_ACTIVE = 0x00,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_SCAN_ACTIVE = 0x01,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_PRESCAN_ACTIVE = 0x02,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_FATAL = 0x03,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_PATTERN_VENDOR_SPECIFIC = 0x04,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_PLIST = 0x05,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_VENDOR_SPECIFIC = 0x06,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_TEMPERATURE = 0x07,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_BMS_TIMER = 0x08,
|
||
|
+} BDSmartSCSIBackgroundScanStatus;
|
||
|
+
|
||
|
+
|
||
|
+#define BD_SMART_TYPE_SCSI (bd_smart_scsi_get_type ())
|
||
|
+GType bd_smart_scsi_get_type ();
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartSCSI:
|
||
|
+ * @smart_supported: Indicates that the device has SMART capability.
|
||
|
+ * @smart_enabled: Indicates that the SMART support is enabled.
|
||
|
+ * @overall_status_passed: %TRUE if the device SMART overall-health self-assessment test result has passed, %FALSE otherwise with @scsi_ie fields set.
|
||
|
+ * @scsi_ie: The reported SCSI Informational Exception in a simplified form. See #BDSmartSCSIInformationalException.
|
||
|
+ * @scsi_ie_asc: The reported SCSI Informational Exception ASC (Additional Sense Code) value (only values of 0xb - warnings and 0x5d - impending failures are taken in account).
|
||
|
+ * @scsi_ie_ascq: The reported SCSI Informational Exception ASCQ (Additional Sense Code Qualifier) value.
|
||
|
+ * @scsi_ie_string: String representation of the current SCSI Informational Exception.
|
||
|
+ * @background_scan_status: Background scan status, see #BDSmartSCSIBackgroundScanStatus.
|
||
|
+ * @background_scan_progress: Percent of a background scan progress.
|
||
|
+ * @background_scan_runs: Number of background scans performed.
|
||
|
+ * @background_medium_scan_runs: Number of background medium scans performed.
|
||
|
+ * @read_errors_corrected_eccfast: Error counter log - read errors corrected by ECC fast.
|
||
|
+ * @read_errors_corrected_eccdelayed: Error counter log - read errors corrected by ECC delayed.
|
||
|
+ * @read_errors_corrected_rereads: Error counter log - read errors corrected by rereads.
|
||
|
+ * @read_errors_corrected_total: Error counter log - total read errors corrected.
|
||
|
+ * @read_errors_uncorrected: Error counter log - total uncorrected read errors.
|
||
|
+ * @read_processed_bytes: Error counter log - total bytes processed.
|
||
|
+ * @write_errors_corrected_eccfast: Error counter log - write errors corrected by ECC fast.
|
||
|
+ * @write_errors_corrected_eccdelayed: Error counter log - write errors corrected by ECC delayed.
|
||
|
+ * @write_errors_corrected_rewrites: Error counter log - write errors corrected by rewrites.
|
||
|
+ * @write_errors_corrected_total: Error counter log - total write errors corrected.
|
||
|
+ * @write_errors_uncorrected: Error counter log - total uncorrected write errors.
|
||
|
+ * @write_processed_bytes: Error counter log - total bytes processed.
|
||
|
+ * @start_stop_cycle_count: Accumulated start-stop cycles.
|
||
|
+ * @start_stop_cycle_lifetime: Specified cycle count over device lifetime.
|
||
|
+ * @load_unload_cycle_count: Accumulated load-unload cycles.
|
||
|
+ * @load_unload_cycle_lifetime: Specified load-unload count over device lifetime.
|
||
|
+ * @scsi_grown_defect_list: Elements in grown defect list.
|
||
|
+ * @power_on_time: Accumulated power on time in minutes.
|
||
|
+ * @temperature_warning_enabled: Indicates that temperature warning is enabled.
|
||
|
+ * @temperature: The current drive temperature in Kelvin or 0 when temperature is not reported.
|
||
|
+ * @temperature_drive_trip: The drive trip temperature in Kelvin or 0 when temperature is not reported.
|
||
|
+ */
|
||
|
+typedef struct BDSmartSCSI {
|
||
|
+ gboolean smart_supported;
|
||
|
+ gboolean smart_enabled;
|
||
|
+ gboolean overall_status_passed;
|
||
|
+ BDSmartSCSIInformationalException scsi_ie;
|
||
|
+ guint8 scsi_ie_asc;
|
||
|
+ guint8 scsi_ie_ascq;
|
||
|
+ gchar *scsi_ie_string;
|
||
|
+ BDSmartSCSIBackgroundScanStatus background_scan_status;
|
||
|
+ gdouble background_scan_progress;
|
||
|
+ guint background_scan_runs;
|
||
|
+ guint background_medium_scan_runs;
|
||
|
+ guint read_errors_corrected_eccfast;
|
||
|
+ guint read_errors_corrected_eccdelayed;
|
||
|
+ guint read_errors_corrected_rereads;
|
||
|
+ guint read_errors_corrected_total;
|
||
|
+ guint read_errors_uncorrected;
|
||
|
+ guint64 read_processed_bytes;
|
||
|
+ guint write_errors_corrected_eccfast;
|
||
|
+ guint write_errors_corrected_eccdelayed;
|
||
|
+ guint write_errors_corrected_rewrites;
|
||
|
+ guint write_errors_corrected_total;
|
||
|
+ guint write_errors_uncorrected;
|
||
|
+ guint64 write_processed_bytes;
|
||
|
+ guint start_stop_cycle_count;
|
||
|
+ guint start_stop_cycle_lifetime;
|
||
|
+ guint load_unload_cycle_count;
|
||
|
+ guint load_unload_cycle_lifetime;
|
||
|
+ guint scsi_grown_defect_list;
|
||
|
+ guint power_on_time;
|
||
|
+ gboolean temperature_warning_enabled;
|
||
|
+ guint temperature;
|
||
|
+ guint temperature_drive_trip;
|
||
|
+} BDSmartSCSI;
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_scsi_free: (skip)
|
||
|
+ * @data: (nullable): %BDSmartSCSI to free
|
||
|
+ *
|
||
|
+ * Frees @data.
|
||
|
+ */
|
||
|
+void bd_smart_scsi_free (BDSmartSCSI *data) {
|
||
|
+ if (data == NULL)
|
||
|
+ return;
|
||
|
+
|
||
|
+ g_free (data->scsi_ie_string);
|
||
|
+ g_free (data);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_scsi_copy: (skip)
|
||
|
+ * @data: (nullable): %BDSmartSCSI to copy
|
||
|
+ *
|
||
|
+ * Creates a new copy of @data.
|
||
|
+ */
|
||
|
+BDSmartSCSI * bd_smart_scsi_copy (BDSmartSCSI *data) {
|
||
|
+ BDSmartSCSI *new_data;
|
||
|
+
|
||
|
+ if (data == NULL)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ new_data = g_new0 (BDSmartSCSI, 1);
|
||
|
+ memcpy (new_data, data, sizeof (BDSmartSCSI));
|
||
|
+ new_data->scsi_ie_string = g_strdup (data->scsi_ie_string);
|
||
|
+
|
||
|
+ return new_data;
|
||
|
+}
|
||
|
+
|
||
|
+GType bd_smart_scsi_get_type () {
|
||
|
+ static GType type = 0;
|
||
|
+
|
||
|
+ if (G_UNLIKELY (type == 0)) {
|
||
|
+ type = g_boxed_type_register_static ("BDSmartSCSI",
|
||
|
+ (GBoxedCopyFunc) bd_smart_scsi_copy,
|
||
|
+ (GBoxedFreeFunc) bd_smart_scsi_free);
|
||
|
+ }
|
||
|
+ return type;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/* BpG-skip */
|
||
|
+/**
|
||
|
+ * BDSmartSelfTestOp:
|
||
|
+ * @BD_SMART_SELF_TEST_OP_ABORT: Abort a running SMART self-test.
|
||
|
+ * @BD_SMART_SELF_TEST_OP_OFFLINE: SMART Immediate Offline Test in background (ATA) or a default self test in foreground (SCSI).
|
||
|
+ * @BD_SMART_SELF_TEST_OP_SHORT: SMART Short Self Test in background (ATA) or "Background short" self-test (SCSI).
|
||
|
+ * @BD_SMART_SELF_TEST_OP_LONG: SMART Extended Self Test in background (ATA) or "Background long" self-test (SCSI).
|
||
|
+ * @BD_SMART_SELF_TEST_OP_CONVEYANCE: SMART Conveyance Self Test in background (ATA only).
|
||
|
+ */
|
||
|
+/* BpG-skip-end */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_SELF_TEST_OP_ABORT,
|
||
|
+ BD_SMART_SELF_TEST_OP_OFFLINE,
|
||
|
+ BD_SMART_SELF_TEST_OP_SHORT,
|
||
|
+ BD_SMART_SELF_TEST_OP_LONG,
|
||
|
+ BD_SMART_SELF_TEST_OP_CONVEYANCE,
|
||
|
+} BDSmartSelfTestOp;
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_get_info:
|
||
|
+ * @device: device to check.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from the drive.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_get_info (const gchar *device, const BDExtraArg **extra, GError **error);
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_get_info_from_data:
|
||
|
+ * @data: (array length=data_len): binary data to parse.
|
||
|
+ * @data_len: length of the data supplied.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from the supplied data.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_get_info_from_data (const guint8 *data, gsize data_len, GError **error);
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_scsi_get_info:
|
||
|
+ * @device: device to check.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from SCSI or SAS-compliant drive.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): SCSI SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_SCSI-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartSCSI * bd_smart_scsi_get_info (const gchar *device, const BDExtraArg **extra, GError **error);
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_set_enabled:
|
||
|
+ * @device: SMART-capable device.
|
||
|
+ * @enabled: whether to enable or disable the SMART functionality
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Enables or disables SMART functionality on device.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE when the functionality was set successfully or %FALSE in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+gboolean bd_smart_set_enabled (const gchar *device, gboolean enabled, const BDExtraArg **extra, GError **error);
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_device_self_test:
|
||
|
+ * @device: device to trigger the test on.
|
||
|
+ * @operation: #BDSmartSelfTestOp self-test operation.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Executes or aborts device self-test.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE when the self-test was trigerred successfully or %FALSE in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_SELFTEST
|
||
|
+ */
|
||
|
+gboolean bd_smart_device_self_test (const gchar *device, BDSmartSelfTestOp operation, const BDExtraArg **extra, GError **error);
|
||
|
+
|
||
|
+#endif /* BD_SMART_API */
|
||
|
diff --git a/src/lib/plugins.h b/src/lib/plugins.h
|
||
|
index 35749163..c5d0ce9a 100644
|
||
|
--- a/src/lib/plugins.h
|
||
|
+++ b/src/lib/plugins.h
|
||
|
@@ -18,6 +18,7 @@ typedef enum {
|
||
|
BD_PLUGIN_FS,
|
||
|
BD_PLUGIN_NVDIMM,
|
||
|
BD_PLUGIN_NVME,
|
||
|
+ BD_PLUGIN_SMART,
|
||
|
BD_PLUGIN_UNDEF
|
||
|
} BDPlugin;
|
||
|
|
||
|
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
|
||
|
index 71bacb7c..4fa0d20e 100644
|
||
|
--- a/src/plugins/Makefile.am
|
||
|
+++ b/src/plugins/Makefile.am
|
||
|
@@ -8,6 +8,8 @@ if WITH_NVME
|
||
|
SUBDIRS += nvme
|
||
|
endif
|
||
|
|
||
|
+SUBDIRS += smart
|
||
|
+
|
||
|
lib_LTLIBRARIES =
|
||
|
|
||
|
if WITH_BTRFS
|
||
|
@@ -182,6 +184,10 @@ endif
|
||
|
|
||
|
if WITH_LVM
|
||
|
libinclude_HEADERS += lvm.h
|
||
|
+else
|
||
|
+if WITH_LVM_DBUS
|
||
|
+libinclude_HEADERS += lvm.h
|
||
|
+endif
|
||
|
endif
|
||
|
|
||
|
if WITH_MDRAID
|
||
|
diff --git a/src/plugins/smart/Makefile.am b/src/plugins/smart/Makefile.am
|
||
|
new file mode 100644
|
||
|
index 00000000..c473d333
|
||
|
--- /dev/null
|
||
|
+++ b/src/plugins/smart/Makefile.am
|
||
|
@@ -0,0 +1,54 @@
|
||
|
+AUTOMAKE_OPTIONS = subdir-objects
|
||
|
+
|
||
|
+lib_LTLIBRARIES =
|
||
|
+libincludedir = $(includedir)/blockdev
|
||
|
+
|
||
|
+if WITH_SMART
|
||
|
+libinclude_HEADERS = smart.h
|
||
|
+else
|
||
|
+if WITH_SMARTMONTOOLS
|
||
|
+libinclude_HEADERS = smart.h
|
||
|
+endif
|
||
|
+endif
|
||
|
+
|
||
|
+
|
||
|
+if WITH_SMART
|
||
|
+
|
||
|
+lib_LTLIBRARIES += libbd_smart.la
|
||
|
+
|
||
|
+libbd_smart_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(SMART_CFLAGS) $(DRIVEDB_H_CFLAGS) -Wall -Wextra -Werror
|
||
|
+libbd_smart_la_LIBADD = ${builddir}/../../utils/libbd_utils.la $(GLIB_LIBS) $(GIO_LIBS) $(SMART_LIBS)
|
||
|
+libbd_smart_la_LDFLAGS = -L${srcdir}/../../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*'
|
||
|
+libbd_smart_la_CPPFLAGS = -I${builddir}/../../../include/ -I${srcdir}/../ -I. -DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\"
|
||
|
+
|
||
|
+libbd_smart_la_SOURCES = \
|
||
|
+ smart.h \
|
||
|
+ smart-private.h \
|
||
|
+ smart-common.c \
|
||
|
+ drivedb-parser.c \
|
||
|
+ libatasmart.c \
|
||
|
+ ../check_deps.c \
|
||
|
+ ../check_deps.h
|
||
|
+
|
||
|
+endif
|
||
|
+
|
||
|
+
|
||
|
+if WITH_SMARTMONTOOLS
|
||
|
+
|
||
|
+lib_LTLIBRARIES += libbd_smartmontools.la
|
||
|
+
|
||
|
+libbd_smartmontools_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(JSON_GLIB_CFLAGS) $(DRIVEDB_H_CFLAGS) -Wall -Wextra -Werror
|
||
|
+libbd_smartmontools_la_LIBADD = ${builddir}/../../utils/libbd_utils.la $(GLIB_LIBS) $(GIO_LIBS) $(JSON_GLIB_LIBS)
|
||
|
+libbd_smartmontools_la_LDFLAGS = -L${srcdir}/../../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*'
|
||
|
+libbd_smartmontools_la_CPPFLAGS = -I${builddir}/../../../include/ -I${srcdir}/../ -I. -DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\"
|
||
|
+
|
||
|
+libbd_smartmontools_la_SOURCES = \
|
||
|
+ smart.h \
|
||
|
+ smart-private.h \
|
||
|
+ smart-common.c \
|
||
|
+ drivedb-parser.c \
|
||
|
+ smartmontools.c \
|
||
|
+ ../check_deps.c \
|
||
|
+ ../check_deps.h
|
||
|
+
|
||
|
+endif
|
||
|
diff --git a/src/plugins/smart/drivedb-parser.c b/src/plugins/smart/drivedb-parser.c
|
||
|
new file mode 100644
|
||
|
index 00000000..16c0c288
|
||
|
--- /dev/null
|
||
|
+++ b/src/plugins/smart/drivedb-parser.c
|
||
|
@@ -0,0 +1,216 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2014-2024 Red Hat, Inc.
|
||
|
+ *
|
||
|
+ * This library 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.
|
||
|
+ *
|
||
|
+ * This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
|
||
|
+ *
|
||
|
+ * Author: Tomas Bzatek <tbzatek@redhat.com>
|
||
|
+ */
|
||
|
+
|
||
|
+#include <glib.h>
|
||
|
+#include <string.h>
|
||
|
+#include <stdio.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <errno.h>
|
||
|
+
|
||
|
+#include "smart.h"
|
||
|
+#include "smart-private.h"
|
||
|
+
|
||
|
+
|
||
|
+void free_drivedb_attrs (DriveDBAttr **attrs) {
|
||
|
+ DriveDBAttr **a;
|
||
|
+
|
||
|
+ if (attrs == NULL)
|
||
|
+ return;
|
||
|
+ for (a = attrs; *a; a++) {
|
||
|
+ g_free ((*a)->name);
|
||
|
+ g_free (*a);
|
||
|
+ }
|
||
|
+ g_free (attrs);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+#ifndef HAVE_DRIVEDB_H
|
||
|
+DriveDBAttr** drivedb_lookup_drive (G_GNUC_UNUSED const gchar *model, G_GNUC_UNUSED const gchar *fw, G_GNUC_UNUSED gboolean include_defaults) {
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+#else
|
||
|
+
|
||
|
+struct drive_settings {
|
||
|
+ const char* modelfamily;
|
||
|
+ const char* modelregexp;
|
||
|
+ const char* firmwareregexp;
|
||
|
+ const char* warningmsg;
|
||
|
+ const char* presets;
|
||
|
+};
|
||
|
+
|
||
|
+static const struct drive_settings builtin_knowndrives[] = {
|
||
|
+#include <drivedb.h>
|
||
|
+};
|
||
|
+
|
||
|
+
|
||
|
+static gboolean parse_attribute_def (const char *arg, gint *attr_id, gchar **attr_name) {
|
||
|
+ int format_str_len = 0;
|
||
|
+ int attrname_str_len = 0;
|
||
|
+ int hddssd_str_len = 0;
|
||
|
+ char format_str[33] = {0,};
|
||
|
+ char attrname_str[33] = {0,};
|
||
|
+ char hddssd_str[4] = {0,};
|
||
|
+
|
||
|
+ if (arg[0] == 'N')
|
||
|
+ /* ignoring "N,format[,name]" as it doesn't provide attribute ID */
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ /* parse "id,format[+][,name[,HDD|SSD]]" */
|
||
|
+ if (sscanf (arg, "%d,%32[^,]%n,%32[^,]%n,%3[DHS]%n",
|
||
|
+ attr_id,
|
||
|
+ format_str, &format_str_len,
|
||
|
+ attrname_str, &attrname_str_len,
|
||
|
+ hddssd_str, &hddssd_str_len) < 2)
|
||
|
+ return FALSE;
|
||
|
+ if (*attr_id < 1 || *attr_id > 255)
|
||
|
+ return FALSE;
|
||
|
+ if (attrname_str_len < 1)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ /* ignoring format_str */
|
||
|
+ *attr_name = g_strndup (attrname_str, attrname_str_len);
|
||
|
+ /* ignoring hddssd_str */
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+static void parse_presets_str (const char *presets, GHashTable *attrs) {
|
||
|
+ while (TRUE) {
|
||
|
+ char opt;
|
||
|
+ char arg[94] = {0,};
|
||
|
+ int len = 0;
|
||
|
+ gint attr_id = 0;
|
||
|
+ gchar *attr_name = NULL;
|
||
|
+
|
||
|
+ presets += strspn (presets, " \t");
|
||
|
+ if (presets[0] == '\0')
|
||
|
+ break;
|
||
|
+ if (sscanf (presets, "-%c %80[^ ]%n", &opt, arg, &len) < 2)
|
||
|
+ break;
|
||
|
+ if (len < 1)
|
||
|
+ break;
|
||
|
+ if (opt == 'v') {
|
||
|
+ /* parse "-v N,format[,name[,HDD|SSD]]" */
|
||
|
+ if (parse_attribute_def (arg, &attr_id, &attr_name)) {
|
||
|
+ g_hash_table_replace (attrs, GINT_TO_POINTER (attr_id), attr_name);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ /* ignoring other switches like 'F' and 'd' */
|
||
|
+
|
||
|
+ presets += len;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+DriveDBAttr** drivedb_lookup_drive (const gchar *model, const gchar *fw, gboolean include_defaults) {
|
||
|
+ gulong i;
|
||
|
+ GHashTable *attrs;
|
||
|
+ DriveDBAttr **ret = NULL;
|
||
|
+
|
||
|
+ if (G_N_ELEMENTS (builtin_knowndrives) == 0)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ attrs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
|
||
|
+
|
||
|
+ /* first parse the DEFAULTS definitions */
|
||
|
+ if (include_defaults)
|
||
|
+ for (i = 0; i < G_N_ELEMENTS (builtin_knowndrives); i++)
|
||
|
+ if (builtin_knowndrives[i].modelfamily &&
|
||
|
+ builtin_knowndrives[i].presets &&
|
||
|
+ g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "DEFAULT", 7) == 0)
|
||
|
+ parse_presets_str (builtin_knowndrives[i].presets, attrs);
|
||
|
+
|
||
|
+ /* now overlay/replace with drive specific keys */
|
||
|
+ for (i = 0; i < G_N_ELEMENTS (builtin_knowndrives); i++) {
|
||
|
+ GRegex *regex;
|
||
|
+ GError *error = NULL;
|
||
|
+ gboolean match = FALSE;
|
||
|
+
|
||
|
+ /* check the modelfamily string */
|
||
|
+ if (builtin_knowndrives[i].modelfamily == NULL ||
|
||
|
+ builtin_knowndrives[i].modelregexp == NULL ||
|
||
|
+ builtin_knowndrives[i].presets == NULL || strlen (builtin_knowndrives[i].presets) < 5 ||
|
||
|
+ g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "VERSION", 7) == 0 ||
|
||
|
+ g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "USB", 3) == 0 ||
|
||
|
+ g_ascii_strncasecmp (builtin_knowndrives[i].modelfamily, "DEFAULT", 7) == 0)
|
||
|
+ continue;
|
||
|
+ /* assuming modelfamily=ATA from now on... */
|
||
|
+
|
||
|
+ /* match the model string */
|
||
|
+ regex = g_regex_new (builtin_knowndrives[i].modelregexp,
|
||
|
+ 0, 0, &error);
|
||
|
+ if (regex == NULL) {
|
||
|
+ bd_utils_log_format (BD_UTILS_LOG_DEBUG,
|
||
|
+ "drivedb-parser: regex compilation failed for '%s': %s",
|
||
|
+ builtin_knowndrives[i].modelregexp,
|
||
|
+ error->message);
|
||
|
+ g_error_free (error);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ if (g_regex_match (regex, model, 0, NULL))
|
||
|
+ match = TRUE;
|
||
|
+ g_regex_unref (regex);
|
||
|
+ if (!match)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ /* match the firmware string */
|
||
|
+ if (builtin_knowndrives[i].firmwareregexp &&
|
||
|
+ strlen (builtin_knowndrives[i].firmwareregexp) > 0 &&
|
||
|
+ fw && strlen (fw) > 0)
|
||
|
+ {
|
||
|
+ regex = g_regex_new (builtin_knowndrives[i].firmwareregexp,
|
||
|
+ 0, 0, &error);
|
||
|
+ if (regex == NULL) {
|
||
|
+ bd_utils_log_format (BD_UTILS_LOG_DEBUG,
|
||
|
+ "drivedb-parser: regex compilation failed for '%s': %s",
|
||
|
+ builtin_knowndrives[i].firmwareregexp,
|
||
|
+ error->message);
|
||
|
+ g_error_free (error);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ if (!g_regex_match (regex, model, 0, NULL))
|
||
|
+ match = FALSE;
|
||
|
+ g_regex_unref (regex);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (match)
|
||
|
+ parse_presets_str (builtin_knowndrives[i].presets, attrs);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (g_hash_table_size (attrs) > 0) {
|
||
|
+ GHashTableIter iter;
|
||
|
+ gpointer key, val;
|
||
|
+
|
||
|
+ /* convert to NULL-terminated array */
|
||
|
+ i = 0;
|
||
|
+ ret = g_new0 (DriveDBAttr *, g_hash_table_size (attrs) + 1);
|
||
|
+ g_hash_table_iter_init (&iter, attrs);
|
||
|
+ while (g_hash_table_iter_next (&iter, &key, &val)) {
|
||
|
+ DriveDBAttr *attr;
|
||
|
+
|
||
|
+ attr = g_new0 (DriveDBAttr, 1);
|
||
|
+ attr->id = GPOINTER_TO_INT (key);
|
||
|
+ attr->name = g_strdup (val);
|
||
|
+ ret[i++] = attr;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ g_hash_table_destroy (attrs);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+#endif /* HAVE_DRIVEDB_H */
|
||
|
diff --git a/src/plugins/smart/libatasmart.c b/src/plugins/smart/libatasmart.c
|
||
|
new file mode 100644
|
||
|
index 00000000..6a805546
|
||
|
--- /dev/null
|
||
|
+++ b/src/plugins/smart/libatasmart.c
|
||
|
@@ -0,0 +1,561 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2014-2024 Red Hat, Inc.
|
||
|
+ *
|
||
|
+ * This library 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.
|
||
|
+ *
|
||
|
+ * This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
|
||
|
+ *
|
||
|
+ * Author: Tomas Bzatek <tbzatek@redhat.com>
|
||
|
+ */
|
||
|
+
|
||
|
+#include <glib.h>
|
||
|
+#include <string.h>
|
||
|
+#include <stdio.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <errno.h>
|
||
|
+
|
||
|
+#include <atasmart.h>
|
||
|
+
|
||
|
+#include <blockdev/utils.h>
|
||
|
+#include <check_deps.h>
|
||
|
+
|
||
|
+#include "smart.h"
|
||
|
+#include "smart-private.h"
|
||
|
+
|
||
|
+/**
|
||
|
+ * SECTION: smart
|
||
|
+ * @short_description: SMART device reporting and management.
|
||
|
+ * @title: SMART
|
||
|
+ * @include: smart.h
|
||
|
+ *
|
||
|
+ * A plugin for SMART device reporting and management, based on libatasmart.
|
||
|
+ */
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_is_tech_avail:
|
||
|
+ * @tech: the queried tech
|
||
|
+ * @mode: a bit mask of queried modes of operation (#BDSmartTechMode) for @tech
|
||
|
+ * @error: (out) (nullable): place to store error (details about why the @tech-@mode combination is not available)
|
||
|
+ *
|
||
|
+ * Returns: whether the @tech-@mode combination is available -- supported by the
|
||
|
+ * plugin implementation and having all the runtime dependencies available
|
||
|
+ */
|
||
|
+gboolean bd_smart_is_tech_avail (G_GNUC_UNUSED BDSmartTech tech, G_GNUC_UNUSED guint64 mode, GError **error) {
|
||
|
+ switch (tech) {
|
||
|
+ case BD_SMART_TECH_ATA:
|
||
|
+ return TRUE;
|
||
|
+ case BD_SMART_TECH_SCSI:
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "SCSI SMART is unavailable with libatasmart");
|
||
|
+ return FALSE;
|
||
|
+ default:
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "Unknown technology");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/* copied from libatasmart/atasmart.c (a non-public symbol) */
|
||
|
+static gchar *print_value (uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
|
||
|
+ switch (pretty_unit) {
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
|
||
|
+ if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
|
||
|
+ return g_strdup_printf ("%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
|
||
|
+ if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
|
||
|
+ return g_strdup_printf ("%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
|
||
|
+ if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
|
||
|
+ return g_strdup_printf ("%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
|
||
|
+ if (pretty_value >= 1000LLU*60LLU*60LLU)
|
||
|
+ return g_strdup_printf ("%0.1f h", ((double) pretty_value)/(1000.0*60*60));
|
||
|
+ if (pretty_value >= 1000LLU*60LLU)
|
||
|
+ return g_strdup_printf ("%0.1f min", ((double) pretty_value)/(1000.0*60));
|
||
|
+ if (pretty_value >= 1000LLU)
|
||
|
+ return g_strdup_printf ("%0.1f s", ((double) pretty_value)/(1000.0));
|
||
|
+ else
|
||
|
+ return g_strdup_printf ("%llu ms", (unsigned long long) pretty_value);
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
|
||
|
+ return g_strdup_printf ("%0.1f C", ((double) pretty_value - 273150) / 1000);
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
|
||
|
+ return g_strdup_printf ("%llu sectors", (unsigned long long) pretty_value);
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
|
||
|
+ return g_strdup_printf ("%llu%%", (unsigned long long) pretty_value);
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
|
||
|
+ return g_strdup_printf ("%0.3f%%", (double) pretty_value);
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_MB:
|
||
|
+ if (pretty_value >= 1000000LLU)
|
||
|
+ return g_strdup_printf ("%0.3f TB", (double) pretty_value / 1000000LLU);
|
||
|
+ if (pretty_value >= 1000LLU)
|
||
|
+ return g_strdup_printf ("%0.3f GB", (double) pretty_value / 1000LLU);
|
||
|
+ else
|
||
|
+ return g_strdup_printf ("%llu MB", (unsigned long long) pretty_value);
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_NONE:
|
||
|
+ return g_strdup_printf ("%llu", (unsigned long long) pretty_value);
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
|
||
|
+ return g_strdup_printf ("n/a");
|
||
|
+ break;
|
||
|
+ case _SK_SMART_ATTRIBUTE_UNIT_MAX:
|
||
|
+ g_warn_if_reached ();
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+struct ParseTempData {
|
||
|
+ guint attr_id;
|
||
|
+ guint64 value;
|
||
|
+ DriveDBAttr **drivedb_attrs;
|
||
|
+};
|
||
|
+
|
||
|
+static void parse_temp_attr_cb (G_GNUC_UNUSED SkDisk *d,
|
||
|
+ const SkSmartAttributeParsedData *a,
|
||
|
+ void *user_data) {
|
||
|
+ struct ParseTempData *data = user_data;
|
||
|
+
|
||
|
+ if (a->id != data->attr_id)
|
||
|
+ return;
|
||
|
+ if ((data->attr_id == 194 || data->attr_id == 190) &&
|
||
|
+ a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
|
||
|
+ return;
|
||
|
+ /* TODO: Validate against drivedb when we have backwards mapping
|
||
|
+ * from smartmontools attribute names back to libatasmart names.
|
||
|
+ * At this point the well_known_attrs[] table serves only
|
||
|
+ * for forward validation.
|
||
|
+ */
|
||
|
+ data->value = a->pretty_value;
|
||
|
+}
|
||
|
+
|
||
|
+static guint64 calculate_temperature(SkDisk *d, DriveDBAttr **drivedb_attrs) {
|
||
|
+ const guint temp_attrs[2] = {194, 190, /* 9, 220 */ }; /* as defined in smartmontools/atacmds.cpp:ata_return_temperature_value() */
|
||
|
+ unsigned long int i;
|
||
|
+
|
||
|
+ for (i = 0; i < G_N_ELEMENTS (temp_attrs); i++) {
|
||
|
+ struct ParseTempData parse_data = {
|
||
|
+ .attr_id = temp_attrs[i],
|
||
|
+ .drivedb_attrs = drivedb_attrs,
|
||
|
+ .value = 0,
|
||
|
+ };
|
||
|
+
|
||
|
+ if (sk_disk_smart_parse_attributes (d, parse_temp_attr_cb, &parse_data) != 0)
|
||
|
+ break;
|
||
|
+ if (parse_data.value != 0)
|
||
|
+ return parse_data.value;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+struct ParseData {
|
||
|
+ GPtrArray *ptr_array;
|
||
|
+ const SkIdentifyParsedData *identify_data;
|
||
|
+ DriveDBAttr **drivedb_attrs;
|
||
|
+};
|
||
|
+
|
||
|
+static void parse_attr_cb (G_GNUC_UNUSED SkDisk *d,
|
||
|
+ const SkSmartAttributeParsedData *a,
|
||
|
+ void *user_data) {
|
||
|
+ struct ParseData *data = user_data;
|
||
|
+ BDSmartATAAttribute *attr;
|
||
|
+
|
||
|
+ attr = g_new0 (BDSmartATAAttribute, 1);
|
||
|
+ attr->id = a->id;
|
||
|
+ attr->name = g_strdup (a->name);
|
||
|
+ attr->well_known_name = g_strdup (a->name);
|
||
|
+ attr->value = a->current_value_valid ? a->current_value : -1;
|
||
|
+ attr->worst = a->worst_value_valid ? a->worst_value : -1;
|
||
|
+ attr->threshold = a->threshold_valid ? a->threshold : -1;
|
||
|
+ attr->failed_past = attr->worst > 0 && attr->threshold > 0 && attr->worst <= attr->threshold;
|
||
|
+ attr->failing_now = attr->value > 0 && attr->threshold > 0 && attr->value <= attr->threshold;
|
||
|
+ attr->value_raw = ((uint64_t) a->raw[0]) |
|
||
|
+ (((uint64_t) a->raw[1]) << 8) |
|
||
|
+ (((uint64_t) a->raw[2]) << 16) |
|
||
|
+ (((uint64_t) a->raw[3]) << 24) |
|
||
|
+ (((uint64_t) a->raw[4]) << 32) |
|
||
|
+ (((uint64_t) a->raw[5]) << 40);
|
||
|
+ attr->flags = a->flags;
|
||
|
+ attr->pretty_value = a->pretty_value;
|
||
|
+ switch (a->pretty_unit) {
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN;
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_NONE:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_NONE;
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS;
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS;
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN;
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT;
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT;
|
||
|
+ break;
|
||
|
+ case SK_SMART_ATTRIBUTE_UNIT_MB:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_MB;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN;
|
||
|
+ g_warn_if_reached ();
|
||
|
+ }
|
||
|
+ attr->pretty_value_string = print_value (a->pretty_value, a->pretty_unit);
|
||
|
+
|
||
|
+ /* validate against drivedb */
|
||
|
+ if (data->drivedb_attrs) {
|
||
|
+ DriveDBAttr **l;
|
||
|
+
|
||
|
+ for (l = data->drivedb_attrs; *l; l++) {
|
||
|
+ if ((*l)->id == a->id && well_known_attrs[a->id].libatasmart_name) {
|
||
|
+ const gchar * const *n;
|
||
|
+ gboolean match = FALSE;
|
||
|
+
|
||
|
+ for (n = well_known_attrs[a->id].smartmontools_names; *n; n++)
|
||
|
+ if (g_ascii_strcasecmp ((*l)->name, *n) == 0) {
|
||
|
+ match = TRUE;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!match) {
|
||
|
+ g_free (attr->well_known_name);
|
||
|
+ attr->well_known_name = NULL;
|
||
|
+ bd_utils_log_format (BD_UTILS_LOG_DEBUG,
|
||
|
+ "libatasmart: drive %s: attribute [%d] \"%s\"/\"%s\" not whitelisted, reporting unknown attr",
|
||
|
+ data->identify_data->model, a->id, a->name, (*l)->name);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ g_ptr_array_add (data->ptr_array, attr);
|
||
|
+}
|
||
|
+
|
||
|
+static BDSmartATA * parse_sk_data (SkDisk *d, GError **error) {
|
||
|
+ SkBool good = FALSE;
|
||
|
+ SkBool available = FALSE;
|
||
|
+ SkSmartOverall overall = SK_SMART_OVERALL_GOOD;
|
||
|
+ uint64_t power_on_msec = 0;
|
||
|
+ const SkSmartParsedData *parsed_data;
|
||
|
+ BDSmartATA *data;
|
||
|
+ GPtrArray *ptr_array;
|
||
|
+ struct ParseData parse_data = {0,};
|
||
|
+
|
||
|
+ if (sk_disk_smart_read_data (d) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error reading SMART data from device: %s",
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (sk_disk_smart_status (d, &good) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error checking SMART data status: %s",
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (sk_disk_smart_parse (d, &parsed_data) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error parsing SMART data: %s",
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ data = g_new0 (BDSmartATA, 1);
|
||
|
+
|
||
|
+ sk_disk_smart_is_available (d, &available);
|
||
|
+ data->smart_supported = available;
|
||
|
+ /* At this point when SMART is not and cannot be enabled,
|
||
|
+ * sk_disk_smart_read_data() would've already returned an error.
|
||
|
+ */
|
||
|
+ data->smart_enabled = TRUE;
|
||
|
+
|
||
|
+ sk_disk_smart_get_overall (d, &overall);
|
||
|
+ data->overall_status_passed = overall == SK_SMART_OVERALL_GOOD;
|
||
|
+
|
||
|
+ switch (parsed_data->offline_data_collection_status) {
|
||
|
+ case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED;
|
||
|
+ break;
|
||
|
+ case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR;
|
||
|
+ break;
|
||
|
+ case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS;
|
||
|
+ break;
|
||
|
+ case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR;
|
||
|
+ break;
|
||
|
+ case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR;
|
||
|
+ break;
|
||
|
+ case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR;
|
||
|
+ break;
|
||
|
+ case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ g_warn_if_reached ();
|
||
|
+ }
|
||
|
+
|
||
|
+ data->auto_offline_data_collection_enabled = FALSE; /* TODO */
|
||
|
+ data->offline_data_collection_completion = parsed_data->total_offline_data_collection_seconds;
|
||
|
+ data->offline_data_collection_capabilities = 0; /* TODO */
|
||
|
+
|
||
|
+ switch (parsed_data->self_test_execution_status) {
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_FATAL;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING;
|
||
|
+ break;
|
||
|
+ case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ g_warn_if_reached ();
|
||
|
+ }
|
||
|
+
|
||
|
+ data->self_test_percent_remaining = parsed_data->self_test_execution_percent_remaining;
|
||
|
+ data->self_test_polling_short = parsed_data->short_test_polling_minutes;
|
||
|
+ data->self_test_polling_extended = parsed_data->extended_test_polling_minutes;
|
||
|
+ data->self_test_polling_conveyance = parsed_data->conveyance_test_polling_minutes;
|
||
|
+
|
||
|
+ data->smart_capabilities = 0; /* TODO */
|
||
|
+
|
||
|
+ sk_disk_smart_get_power_on (d, &power_on_msec);
|
||
|
+ data->power_on_time = power_on_msec / 1000 / 60;
|
||
|
+
|
||
|
+ sk_disk_smart_get_power_cycle (d, &data->power_cycle_count);
|
||
|
+
|
||
|
+ ptr_array = g_ptr_array_new_full (0, (GDestroyNotify) bd_smart_ata_attribute_free);
|
||
|
+ parse_data.ptr_array = ptr_array;
|
||
|
+#ifdef HAVE_DRIVEDB_H
|
||
|
+ sk_disk_identify_parse (d, &parse_data.identify_data);
|
||
|
+ if (parse_data.identify_data)
|
||
|
+ parse_data.drivedb_attrs = drivedb_lookup_drive (parse_data.identify_data->model,
|
||
|
+ parse_data.identify_data->firmware,
|
||
|
+ FALSE /* include_defaults */);
|
||
|
+#endif
|
||
|
+ data->temperature = calculate_temperature (d, parse_data.drivedb_attrs) / 1000;
|
||
|
+
|
||
|
+ if (sk_disk_smart_parse_attributes (d, parse_attr_cb, &parse_data) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error parsing SMART data: %s",
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ g_ptr_array_free (ptr_array, TRUE);
|
||
|
+ bd_smart_ata_free (data);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ free_drivedb_attrs (parse_data.drivedb_attrs);
|
||
|
+ g_ptr_array_add (ptr_array, NULL);
|
||
|
+ data->attributes = (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);
|
||
|
+
|
||
|
+ return data;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_get_info:
|
||
|
+ * @device: device to check.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from the drive.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_get_info (const gchar *device, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
|
||
|
+ SkDisk *d;
|
||
|
+ BDSmartATA *data;
|
||
|
+
|
||
|
+ g_warn_if_fail (device != NULL);
|
||
|
+
|
||
|
+ if (sk_disk_open (device, &d) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error opening device %s: %s",
|
||
|
+ device,
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ data = parse_sk_data (d, error);
|
||
|
+ sk_disk_free (d);
|
||
|
+
|
||
|
+ return data;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_get_info_from_data:
|
||
|
+ * @data: (array length=data_len): binary data to parse.
|
||
|
+ * @data_len: length of the data supplied.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from the supplied data.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_get_info_from_data (const guint8 *data, gsize data_len, GError **error) {
|
||
|
+ SkDisk *d;
|
||
|
+ BDSmartATA *ata_data;
|
||
|
+
|
||
|
+ g_warn_if_fail (data != NULL);
|
||
|
+ g_warn_if_fail (data_len > 0);
|
||
|
+
|
||
|
+ if (sk_disk_open (NULL, &d) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error parsing blob data: %s",
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (sk_disk_set_blob (d, data, data_len) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error parsing blob data: %s",
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ ata_data = parse_sk_data (d, error);
|
||
|
+ sk_disk_free (d);
|
||
|
+
|
||
|
+ return ata_data;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_scsi_get_info:
|
||
|
+ * @device: device to check.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from SCSI or SAS-compliant drive.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): SCSI SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_SCSI-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartSCSI * bd_smart_scsi_get_info (G_GNUC_UNUSED const gchar *device, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "SCSI SMART is unavailable with libatasmart");
|
||
|
+ return FALSE;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_set_enabled:
|
||
|
+ * @device: SMART-capable device.
|
||
|
+ * @enabled: whether to enable or disable the SMART functionality
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Enables or disables SMART functionality on device.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE when the functionality was set successfully or %FALSE in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+gboolean bd_smart_set_enabled (G_GNUC_UNUSED const gchar *device, G_GNUC_UNUSED gboolean enabled, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "Enabling/disabling ATA SMART functionality is unavailable with libatasmart");
|
||
|
+ return FALSE;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_device_self_test:
|
||
|
+ * @device: device to trigger the test on.
|
||
|
+ * @operation: #BDSmartSelfTestOp self-test operation.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Executes or aborts device self-test.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE when the self-test was trigerred successfully or %FALSE in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_SELFTEST
|
||
|
+ */
|
||
|
+gboolean bd_smart_device_self_test (const gchar *device, BDSmartSelfTestOp operation, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
|
||
|
+ SkDisk *d;
|
||
|
+ SkSmartSelfTest op;
|
||
|
+ gboolean ret;
|
||
|
+
|
||
|
+ switch (operation) {
|
||
|
+ case BD_SMART_SELF_TEST_OP_ABORT:
|
||
|
+ op = SK_SMART_SELF_TEST_ABORT;
|
||
|
+ break;
|
||
|
+ case BD_SMART_SELF_TEST_OP_SHORT:
|
||
|
+ op = SK_SMART_SELF_TEST_SHORT;
|
||
|
+ break;
|
||
|
+ case BD_SMART_SELF_TEST_OP_LONG:
|
||
|
+ case BD_SMART_SELF_TEST_OP_OFFLINE:
|
||
|
+ op = SK_SMART_SELF_TEST_EXTENDED;
|
||
|
+ break;
|
||
|
+ case BD_SMART_SELF_TEST_OP_CONVEYANCE:
|
||
|
+ op = SK_SMART_SELF_TEST_CONVEYANCE;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ "Invalid self-test operation.");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (sk_disk_open (device, &d) != 0) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error opening device %s: %s",
|
||
|
+ device,
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = sk_disk_smart_self_test (d, op) == 0;
|
||
|
+ sk_disk_free (d);
|
||
|
+ if (!ret) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ "Error triggering device self-test: %s",
|
||
|
+ strerror_l (errno, _C_LOCALE));
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
diff --git a/src/plugins/smart/smart-common.c b/src/plugins/smart/smart-common.c
|
||
|
new file mode 100644
|
||
|
index 00000000..71576f9e
|
||
|
--- /dev/null
|
||
|
+++ b/src/plugins/smart/smart-common.c
|
||
|
@@ -0,0 +1,175 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2014-2024 Red Hat, Inc.
|
||
|
+ *
|
||
|
+ * This library 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.
|
||
|
+ *
|
||
|
+ * This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
|
||
|
+ *
|
||
|
+ * Author: Tomas Bzatek <tbzatek@redhat.com>
|
||
|
+ */
|
||
|
+
|
||
|
+#include <glib.h>
|
||
|
+#include <string.h>
|
||
|
+#include <stdio.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <errno.h>
|
||
|
+
|
||
|
+#include <blockdev/utils.h>
|
||
|
+#include <check_deps.h>
|
||
|
+
|
||
|
+#include "smart.h"
|
||
|
+#include "smart-private.h"
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_error_quark: (skip)
|
||
|
+ */
|
||
|
+GQuark bd_smart_error_quark (void)
|
||
|
+{
|
||
|
+ return g_quark_from_static_string ("g-bd-smart-error-quark");
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_init:
|
||
|
+ *
|
||
|
+ * Initializes the plugin. **This function is called automatically by the
|
||
|
+ * library's initialization functions.**
|
||
|
+ *
|
||
|
+ */
|
||
|
+gboolean bd_smart_init (void) {
|
||
|
+ /* nothing to do here */
|
||
|
+ return TRUE;
|
||
|
+};
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_close:
|
||
|
+ *
|
||
|
+ * Cleans up after the plugin. **This function is called automatically by the
|
||
|
+ * library's functions that unload it.**
|
||
|
+ *
|
||
|
+ */
|
||
|
+void bd_smart_close (void) {
|
||
|
+ /* nothing to do here */
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_attribute_free: (skip)
|
||
|
+ * @attr: (nullable): %BDSmartATAAttribute to free
|
||
|
+ *
|
||
|
+ * Frees @attr.
|
||
|
+ */
|
||
|
+void bd_smart_ata_attribute_free (BDSmartATAAttribute *attr) {
|
||
|
+ if (attr == NULL)
|
||
|
+ return;
|
||
|
+ g_free (attr->name);
|
||
|
+ g_free (attr->well_known_name);
|
||
|
+ g_free (attr->pretty_value_string);
|
||
|
+ g_free (attr);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_attribute_copy: (skip)
|
||
|
+ * @attr: (nullable): %BDSmartATAAttribute to copy
|
||
|
+ *
|
||
|
+ * Creates a new copy of @attr.
|
||
|
+ */
|
||
|
+BDSmartATAAttribute * bd_smart_ata_attribute_copy (BDSmartATAAttribute *attr) {
|
||
|
+ BDSmartATAAttribute *new_attr;
|
||
|
+
|
||
|
+ if (attr == NULL)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ new_attr = g_new0 (BDSmartATAAttribute, 1);
|
||
|
+ memcpy (new_attr, attr, sizeof (BDSmartATAAttribute));
|
||
|
+ new_attr->name = g_strdup (attr->name);
|
||
|
+ new_attr->well_known_name = g_strdup (attr->well_known_name);
|
||
|
+ new_attr->pretty_value_string = g_strdup (attr->pretty_value_string);
|
||
|
+
|
||
|
+ return new_attr;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_free: (skip)
|
||
|
+ * @data: (nullable): %BDSmartATA to free
|
||
|
+ *
|
||
|
+ * Frees @data.
|
||
|
+ */
|
||
|
+void bd_smart_ata_free (BDSmartATA *data) {
|
||
|
+ BDSmartATAAttribute **attr;
|
||
|
+
|
||
|
+ if (data == NULL)
|
||
|
+ return;
|
||
|
+
|
||
|
+ for (attr = data->attributes; attr && *attr; attr++)
|
||
|
+ bd_smart_ata_attribute_free (*attr);
|
||
|
+ g_free (data->attributes);
|
||
|
+ g_free (data);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_copy: (skip)
|
||
|
+ * @data: (nullable): %BDSmartATA to copy
|
||
|
+ *
|
||
|
+ * Creates a new copy of @data.
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_copy (BDSmartATA *data) {
|
||
|
+ BDSmartATA *new_data;
|
||
|
+ BDSmartATAAttribute **attr;
|
||
|
+ GPtrArray *ptr_array;
|
||
|
+
|
||
|
+ if (data == NULL)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ new_data = g_new0 (BDSmartATA, 1);
|
||
|
+ memcpy (new_data, data, sizeof (BDSmartATA));
|
||
|
+
|
||
|
+ ptr_array = g_ptr_array_new ();
|
||
|
+ for (attr = data->attributes; attr && *attr; attr++)
|
||
|
+ g_ptr_array_add (ptr_array, bd_smart_ata_attribute_copy (*attr));
|
||
|
+ g_ptr_array_add (ptr_array, NULL);
|
||
|
+ new_data->attributes = (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);
|
||
|
+
|
||
|
+ return new_data;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_scsi_free: (skip)
|
||
|
+ * @data: (nullable): %BDSmartSCSI to free
|
||
|
+ *
|
||
|
+ * Frees @data.
|
||
|
+ */
|
||
|
+void bd_smart_scsi_free (BDSmartSCSI *data) {
|
||
|
+ if (data == NULL)
|
||
|
+ return;
|
||
|
+
|
||
|
+ g_free (data->scsi_ie_string);
|
||
|
+ g_free (data);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_scsi_copy: (skip)
|
||
|
+ * @data: (nullable): %BDSmartSCSI to copy
|
||
|
+ *
|
||
|
+ * Creates a new copy of @data.
|
||
|
+ */
|
||
|
+BDSmartSCSI * bd_smart_scsi_copy (BDSmartSCSI *data) {
|
||
|
+ BDSmartSCSI *new_data;
|
||
|
+
|
||
|
+ if (data == NULL)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ new_data = g_new0 (BDSmartSCSI, 1);
|
||
|
+ memcpy (new_data, data, sizeof (BDSmartSCSI));
|
||
|
+ new_data->scsi_ie_string = g_strdup (data->scsi_ie_string);
|
||
|
+
|
||
|
+ return new_data;
|
||
|
+}
|
||
|
diff --git a/src/plugins/smart/smart-private.h b/src/plugins/smart/smart-private.h
|
||
|
new file mode 100644
|
||
|
index 00000000..77207be5
|
||
|
--- /dev/null
|
||
|
+++ b/src/plugins/smart/smart-private.h
|
||
|
@@ -0,0 +1,104 @@
|
||
|
+#include <glib.h>
|
||
|
+#include <glib-object.h>
|
||
|
+#include <blockdev/utils.h>
|
||
|
+
|
||
|
+#include "smart.h"
|
||
|
+
|
||
|
+#ifndef BD_SMART_PRIVATE
|
||
|
+#define BD_SMART_PRIVATE
|
||
|
+
|
||
|
+/* "C" locale to get the locale-agnostic error messages */
|
||
|
+#define _C_LOCALE (locale_t) 0
|
||
|
+
|
||
|
+typedef struct DriveDBAttr {
|
||
|
+ gint id;
|
||
|
+ gchar *name;
|
||
|
+} DriveDBAttr;
|
||
|
+
|
||
|
+struct WellKnownAttrInfo {
|
||
|
+ const gchar *libatasmart_name;
|
||
|
+ BDSmartATAAttributeUnit unit;
|
||
|
+ const gchar *smartmontools_names[7]; /* NULL-terminated */
|
||
|
+};
|
||
|
+
|
||
|
+/* This table was initially stolen from libatasmart, including the comment below: */
|
||
|
+/* This data is stolen from smartmontools */
|
||
|
+static const struct WellKnownAttrInfo well_known_attrs[256] = {
|
||
|
+ [1] = { "raw-read-error-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Raw_Read_Error_Count", "Raw_Read_Error_Rate", NULL }},
|
||
|
+ [2] = { "throughput-performance", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Throughput_Performance", NULL }},
|
||
|
+ [3] = { "spin-up-time", BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS, { "Spin_Up_Time", NULL }},
|
||
|
+ [4] = { "start-stop-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Start_Stop_Count", NULL }},
|
||
|
+ [5] = { "reallocated-sector-count", BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS, { "Reallocated_Block_Count", "Reallocated_Sector_Ct", NULL }},
|
||
|
+ [6] = { "read-channel-margin", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Read_Channel_Margin", NULL }},
|
||
|
+ [7] = { "seek-error-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Seek_Error_Rate", NULL }},
|
||
|
+ [8] = { "seek-time-performance", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Seek_Time_Performance", NULL }},
|
||
|
+ [9] = { "power-on-hours", BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS, { "Power_On_Hours", "Power_On_Hours_and_Msec", NULL }},
|
||
|
+ [10] = { "spin-retry-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Spin_Retry_Count", NULL }},
|
||
|
+ [11] = { "calibration-retry-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Calibration_Retry_Count", NULL }},
|
||
|
+ [12] = { "power-cycle-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Power_Cycle_Count", "Device_Power_Cycle_Cnt", NULL }},
|
||
|
+ [13] = { "read-soft-error-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Read_Soft_Error_Rate", NULL }},
|
||
|
+ [170] = { "available-reserved-space", BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT, { "Available_Reservd_Space", "Reserved_Block_Pct", NULL }},
|
||
|
+ [171] = { "program-fail-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Program_Fail_Cnt", "Program_Fail_Count", "Program_Fail_Ct", NULL }},
|
||
|
+ [172] = { "erase-fail-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Erase_Fail_Cnt", "Erase_Fail_Ct", "Erase_Fail_Count", "Block_Erase_Failure", NULL }},
|
||
|
+ [175] = { "program-fail-count-chip", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Program_Fail_Count_Chip", NULL }},
|
||
|
+ [176] = { "erase-fail-count-chip", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Erase_Fail_Count_Chip", NULL }},
|
||
|
+ [177] = { "wear-leveling-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Wear_Leveling_Count", NULL }},
|
||
|
+ [178] = { "used-reserved-blocks-chip", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Used_Rsvd_Blk_Cnt_Chip", "Used_Rsrvd_Blk_Cnt_Wrst", NULL }},
|
||
|
+ [179] = { "used-reserved-blocks-total", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Used_Rsvd_Blk_Cnt_Tot", "Used_Rsrvd_Blk_Cnt_Tot", NULL }},
|
||
|
+ [180] = { "unused-reserved-blocks", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Unused_Rsvd_Blk_Cnt_Tot", NULL }},
|
||
|
+ [181] = { "program-fail-count-total", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Program_Fail_Cnt_Total", NULL }},
|
||
|
+ [182] = { "erase-fail-count-total", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Erase_Fail_Count_Total", NULL }},
|
||
|
+ [183] = { "runtime-bad-block-total", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Runtime_Bad_Block", NULL }},
|
||
|
+ [184] = { "end-to-end-error", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "End-to-End_Error", "End-to-End_Error_Count", NULL }},
|
||
|
+ [187] = { "reported-uncorrect", BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS, { "Reported_Uncorrect", "Reported_UE_Counts", NULL }},
|
||
|
+ [188] = { "command-timeout", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Command_Timeout", "Command_Timeouts", NULL }},
|
||
|
+ [189] = { "high-fly-writes", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "High_Fly_Writes", NULL }},
|
||
|
+ [190] = { "airflow-temperature-celsius", BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN, { "Airflow_Temperature_Cel", "Case_Temperature", "Drive_Temperature", "Temperature_Case", "Drive_Temp_Warning", "Temperature_Celsius", NULL }},
|
||
|
+ [191] = { "g-sense-error-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "G-Sense_Error_Rate", NULL }},
|
||
|
+ [192] = { "power-off-retract-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Power-Off_Retract_Count", "Power-off_Retract_Count", NULL }},
|
||
|
+ [193] = { "load-cycle-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Load_Cycle_Count", NULL }},
|
||
|
+ [194] = { "temperature-celsius-2", BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN, { "Temperature_Celsius", "Device_Temperature", "Drive_Temperature", "Temperature_Internal", NULL }},
|
||
|
+ [195] = { "hardware-ecc-recovered", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Hardware_ECC_Recovered", "Cumulativ_Corrected_ECC", "ECC_Error_Rate", NULL }},
|
||
|
+ [196] = { "reallocated-event-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Reallocated_Event_Count", NULL }},
|
||
|
+ [197] = { "current-pending-sector", BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS, { "Current_Pending_Sector", "Pending_Sector_Count", NULL }},
|
||
|
+ [198] = { "offline-uncorrectable", BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS, { "Offline_Uncorrectable", "Uncor_Read_Error_Ct", "Uncorrectable_Sector_Ct", NULL }},
|
||
|
+ [199] = { "udma-crc-error-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "CRC_Error_Count", "SATA_CRC_Error", "SATA_CRC_Error_Count", "UDMA_CRC_Error_Count", NULL }},
|
||
|
+ [200] = { "multi-zone-error-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Multi_Zone_Error_Rate", NULL }},
|
||
|
+ [201] = { "soft-read-error-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Soft_Read_Error_Rate", "Read_Error_Rate", "Uncorr_Soft_Read_Err_Rt", "Unc_Read_Error_Rate", "Unc_Soft_Read_Err_Rate", NULL }},
|
||
|
+ [202] = { "ta-increase-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Data_Address_Mark_Errs", NULL }},
|
||
|
+ [203] = { "run-out-cancel", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Run_Out_Cancel", NULL }},
|
||
|
+ [204] = { "shock-count-write-open", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Soft_ECC_Correction", "Soft_ECC_Correction_Rt", "Soft_ECC_Correct_Rate", NULL }},
|
||
|
+ [205] = { "shock-rate-write-open", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Thermal_Asperity_Rate", NULL }},
|
||
|
+ [206] = { "flying-height", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Flying_Height", NULL }},
|
||
|
+ [207] = { "spin-high-current", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Spin_High_Current", NULL }},
|
||
|
+ [208] = { "spin-buzz", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Spin_Buzz", NULL }},
|
||
|
+ [209] = { "offline-seek-performance", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Offline_Seek_Performnce", NULL }},
|
||
|
+ [220] = { "disk-shift", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Disk_Shift", NULL }},
|
||
|
+ [221] = { "g-sense-error-rate-2", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "G-Sense_Error_Rate", NULL }},
|
||
|
+ [222] = { "loaded-hours", BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS, { "Loaded_Hours", NULL }},
|
||
|
+ [223] = { "load-retry-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Load_Retry_Count", NULL }},
|
||
|
+ [224] = { "load-friction", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Load_Friction", NULL }},
|
||
|
+ [225] = { "load-cycle-count-2", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Load_Cycle_Count", NULL }},
|
||
|
+ [226] = { "load-in-time", BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS, { "Load-in_Time", NULL }},
|
||
|
+ [227] = { "torq-amp-count", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Torq-amp_Count", NULL }},
|
||
|
+ [228] = { "power-off-retract-count-2", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Power-Off_Retract_Count", "Power-off_Retract_Count", NULL }},
|
||
|
+ [230] = { "head-amplitude", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Head_Amplitude", NULL }},
|
||
|
+ [231] = { "temperature-celsius", BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN, { "Temperature_Celsius", "Controller_Temperature", NULL }},
|
||
|
+ [232] = { "endurance-remaining", BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT, { "Spares_Remaining_Perc", "Perc_Avail_Resrvd_Space", "Available_Reservd_Space", NULL }},
|
||
|
+ [233] = { "power-on-seconds-2", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { /* TODO */ NULL }},
|
||
|
+ [234] = { "uncorrectable-ecc-count", BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS, { /* TODO */ NULL }},
|
||
|
+ [235] = { "good-block-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN, { "Good/Sys_Block_Count", NULL }},
|
||
|
+ [240] = { "head-flying-hours", BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS, { "Head_Flying_Hours", NULL }},
|
||
|
+ [241] = { "total-lbas-written", BD_SMART_ATA_ATTRIBUTE_UNIT_MB, { /* TODO: implement size calculation logic */ NULL }},
|
||
|
+ [242] = { "total-lbas-read", BD_SMART_ATA_ATTRIBUTE_UNIT_MB, { /* TODO: implement size calculation logic */ NULL }},
|
||
|
+ [250] = { "read-error-retry-rate", BD_SMART_ATA_ATTRIBUTE_UNIT_NONE, { "Read_Error_Retry_Rate", "Read_Retry_Count", NULL }},
|
||
|
+};
|
||
|
+
|
||
|
+
|
||
|
+G_GNUC_INTERNAL
|
||
|
+DriveDBAttr** drivedb_lookup_drive (const gchar *model, const gchar *fw, gboolean include_defaults);
|
||
|
+
|
||
|
+G_GNUC_INTERNAL
|
||
|
+void free_drivedb_attrs (DriveDBAttr **attrs);
|
||
|
+
|
||
|
+#endif /* BD_SMART_PRIVATE */
|
||
|
diff --git a/src/plugins/smart/smart.h b/src/plugins/smart/smart.h
|
||
|
new file mode 100644
|
||
|
index 00000000..0ab748a1
|
||
|
--- /dev/null
|
||
|
+++ b/src/plugins/smart/smart.h
|
||
|
@@ -0,0 +1,460 @@
|
||
|
+#include <glib.h>
|
||
|
+#include <glib-object.h>
|
||
|
+#include <blockdev/utils.h>
|
||
|
+
|
||
|
+#ifndef BD_SMART
|
||
|
+#define BD_SMART
|
||
|
+
|
||
|
+GQuark bd_smart_error_quark (void);
|
||
|
+#define BD_SMART_ERROR bd_smart_error_quark ()
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartError:
|
||
|
+ * @BD_SMART_ERROR_TECH_UNAVAIL: SMART support not available.
|
||
|
+ * @BD_SMART_ERROR_FAILED: General error.
|
||
|
+ * @BD_SMART_ERROR_INVALID_ARGUMENT: Invalid argument.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ERROR_TECH_UNAVAIL,
|
||
|
+ BD_SMART_ERROR_FAILED,
|
||
|
+ BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+} BDSmartError;
|
||
|
+
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_TECH_ATA = 0,
|
||
|
+ BD_SMART_TECH_SCSI = 1,
|
||
|
+} BDSmartTech;
|
||
|
+
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_TECH_MODE_INFO = 1 << 0,
|
||
|
+ BD_SMART_TECH_MODE_SELFTEST = 1 << 1,
|
||
|
+} BDSmartTechMode;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATAOfflineDataCollectionStatus:
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED: Offline data collection activity was never started.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR: Offline data collection activity was completed without error. Indicates a passed test.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS: Offline data collection activity is in progress.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR: Offline data collection activity was suspended by an interrupting command from host.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR: Offline data collection activity was aborted by an interrupting command from host.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR: Offline data collection activity was aborted by the device with a fatal error. Indicates a failed test.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC: Offline data collection activity is in a Vendor Specific state.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED: Offline data collection activity is in a Reserved state.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED = 0x00,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR = 0x02,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS = 0x03,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR = 0x04,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR = 0x05,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR = 0x06,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC = 0x40,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED = 0x3F,
|
||
|
+} BDSmartATAOfflineDataCollectionStatus;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATAOfflineDataCollectionCapabilities:
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_NOT_SUPPORTED: Offline data collection not supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_EXEC_OFFLINE_IMMEDIATE: Execute Offline Immediate function supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_ABORT: Abort Offline collection upon new command.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_SURFACE_SCAN: Offline surface scan supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELF_TEST: Self-test supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_CONVEYANCE_SELF_TEST: Conveyance Self-test supported.
|
||
|
+ * @BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELECTIVE_SELF_TEST: Selective Self-test supported.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_NOT_SUPPORTED = 0x00,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_EXEC_OFFLINE_IMMEDIATE = 0x01,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_ABORT = 0x04,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_SURFACE_SCAN = 0x08,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELF_TEST = 0x10,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_CONVEYANCE_SELF_TEST = 0x20,
|
||
|
+ BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELECTIVE_SELF_TEST = 0x40,
|
||
|
+} BDSmartATAOfflineDataCollectionCapabilities;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATASelfTestStatus:
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR: The previous self-test routine completed without error or no self-test has ever been run.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST: The self-test routine was aborted by the host.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET: The self-test routine was interrupted by the host with a hard or soft reset.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_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.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN: The previous self-test completed having a test element that failed and the test element that failed is not known.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL: The previous self-test completed having the electrical element of the test failed.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO: The previous self-test completed having the servo (and/or seek) element of the test failed.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ: The previous self-test completed having the read element of the test failed.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING: The previous self-test completed having a test element that failed and the device is suspected of having handling damage.
|
||
|
+ * @BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS: Self-test routine in progress.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR = 0x00,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST = 0x01,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET = 0x02,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_FATAL = 0x03,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN = 0x04,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL = 0x05,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO = 0x06,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ = 0x07,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING = 0x08,
|
||
|
+ BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS = 0x0F,
|
||
|
+} BDSmartATASelfTestStatus;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATACapabilities:
|
||
|
+ * @BD_SMART_ATA_CAP_ATTRIBUTE_AUTOSAVE: Saves SMART data before entering power-saving mode.
|
||
|
+ * @BD_SMART_ATA_CAP_AUTOSAVE_TIMER: Supports SMART auto save timer.
|
||
|
+ * @BD_SMART_ATA_CAP_ERROR_LOGGING: Error logging supported.
|
||
|
+ * @BD_SMART_ATA_CAP_GP_LOGGING: General Purpose Logging supported.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_CAP_ATTRIBUTE_AUTOSAVE = 1 << 0,
|
||
|
+ BD_SMART_ATA_CAP_AUTOSAVE_TIMER = 1 << 1,
|
||
|
+ BD_SMART_ATA_CAP_ERROR_LOGGING = 1 << 2,
|
||
|
+ BD_SMART_ATA_CAP_GP_LOGGING = 1 << 3,
|
||
|
+} BDSmartATACapabilities;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATAAttributeUnit:
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN: Unknown.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_NONE: Dimensionless value.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS: Milliseconds.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS: Sectors.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN: Millikelvin.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT: Percentage with 3 decimal points.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT: Integer percentage.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_UNIT_MB: Megabytes.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_NONE,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_UNIT_MB,
|
||
|
+} BDSmartATAAttributeUnit;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATAAttributeFlag:
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_PREFAILURE: Pre-failure/advisory bit: If the value of this bit equals zero, an attribute value less than or equal to its corresponding attribute threshold indicates an advisory condition where the usage or age of the device has exceeded its intended design life period. If the value of this bit equals one, an attribute value less than or equal to its corresponding attribute threshold indicates a prefailure condition where imminent loss of data is being predicted.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_ONLINE: On-line data collection bit: If the value of this bit equals zero, then the attribute value is updated only during off-line data collection activities. If the value of this bit equals one, then the attribute value is updated during normal operation of the device or during both normal operation and off-line testing.
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_PERFORMANCE: Performance type bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_ERROR_RATE: Errorrate type bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_EVENT_COUNT: Eventcount bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_SELF_PRESERVING: Selfpereserving bit (vendor specific).
|
||
|
+ * @BD_SMART_ATA_ATTRIBUTE_FLAG_OTHER: Reserved.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_PREFAILURE = 0x0001,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_ONLINE = 0x0002,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_PERFORMANCE = 0x0004,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_ERROR_RATE = 0x0008,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_EVENT_COUNT = 0x0010,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_SELF_PRESERVING = 0x0020,
|
||
|
+ BD_SMART_ATA_ATTRIBUTE_FLAG_OTHER = 0xffc0,
|
||
|
+} BDSmartATAAttributeFlag;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATAAttribute:
|
||
|
+ * @id: Attribute Identifier.
|
||
|
+ * @name: A free-form representation of the attribute name, implementation-dependent (e.g. libatasmart internal strings or smartmontools' drivedb.h names).
|
||
|
+ * @well_known_name: Translated well-known attribute name (in libatasmart style, e.g. 'raw-read-error-rate') or %NULL in case of unknown, untrusted or vendor-specific value.
|
||
|
+ * @value: The normalized value or -1 if unknown.
|
||
|
+ * @worst: The worst normalized value of -1 if unknown.
|
||
|
+ * @threshold: The threshold of a normalized value or -1 if unknown.
|
||
|
+ * @failed_past: Indicates a failure that happened in the past (the normalized worst value is below the threshold).
|
||
|
+ * @failing_now: Indicates a failure that is happening now (the normalized value is below the threshold).
|
||
|
+ * @value_raw: The raw value of the attribute.
|
||
|
+ * @flags: Bitmask of attribute flags. See #BDSmartATAAttributeFlag.
|
||
|
+ * @pretty_value: Numerical representation of the parsed raw value, presented in @pretty_value_unit units.
|
||
|
+ * @pretty_value_unit: The unit of the parsed raw value.
|
||
|
+ * @pretty_value_string: A free-form string representation of the raw value intended for user presentation or %NULL.
|
||
|
+ */
|
||
|
+typedef struct BDSmartATAAttribute {
|
||
|
+ guint8 id;
|
||
|
+ gchar *name;
|
||
|
+ gchar *well_known_name;
|
||
|
+ gint value;
|
||
|
+ gint worst;
|
||
|
+ gint threshold;
|
||
|
+ gboolean failed_past;
|
||
|
+ gboolean failing_now;
|
||
|
+ guint64 value_raw;
|
||
|
+ guint16 flags;
|
||
|
+ gint64 pretty_value;
|
||
|
+ BDSmartATAAttributeUnit pretty_value_unit;
|
||
|
+ gchar *pretty_value_string;
|
||
|
+} BDSmartATAAttribute;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartATA:
|
||
|
+ * @smart_supported: Indicates that the device has SMART capability.
|
||
|
+ * @smart_enabled: Indicates that the SMART support is enabled.
|
||
|
+ * @overall_status_passed: %TRUE if the device SMART overall-health self-assessment test result has passed.
|
||
|
+ * @offline_data_collection_status: The offline data collection status. See #BDSmartATAOfflineDataCollectionStatus.
|
||
|
+ * @auto_offline_data_collection_enabled: %TRUE if Automatic Offline Data Collection is enabled. Only supported with the smartmontools plugin.
|
||
|
+ * @offline_data_collection_completion: Total time in seconds to complete Offline data collection.
|
||
|
+ * @offline_data_collection_capabilities: Bitmask of offline data collection capabilities, see #BDSmartATAOfflineDataCollectionCapabilities. Only supported with the smartmontools plugin.
|
||
|
+ * @self_test_status: Self-test execution status. See #BDSmartATASelfTestStatus.
|
||
|
+ * @self_test_percent_remaining: The percentage remaining of a running self-test.
|
||
|
+ * @self_test_polling_short: Short self-test routine recommended polling time in minutes or 0 if not supported.
|
||
|
+ * @self_test_polling_extended: Extended self-test routine recommended polling time in minutes or 0 if not supported.
|
||
|
+ * @self_test_polling_conveyance: Conveyance self-test routine recommended polling time in minutes or 0 if not supported.
|
||
|
+ * @smart_capabilities: Bitmask of device misc. SMART capabilities. See #BDSmartATACapabilities. Only supported with the smartmontools plugin.
|
||
|
+ * @attributes: (array zero-terminated=1): A list of reported SMART attributes.
|
||
|
+ * @power_on_time: The count of minutes in power-on state.
|
||
|
+ * @power_cycle_count: The count of full hard disk power on/off cycles.
|
||
|
+ * @temperature: The current drive temperature in Kelvin or 0 when temperature is not reported.
|
||
|
+ */
|
||
|
+typedef struct BDSmartATA {
|
||
|
+ gboolean smart_supported;
|
||
|
+ gboolean smart_enabled;
|
||
|
+ gboolean overall_status_passed;
|
||
|
+ BDSmartATAOfflineDataCollectionStatus offline_data_collection_status;
|
||
|
+ gboolean auto_offline_data_collection_enabled;
|
||
|
+ gint offline_data_collection_completion;
|
||
|
+ guint offline_data_collection_capabilities;
|
||
|
+ BDSmartATASelfTestStatus self_test_status;
|
||
|
+ gint self_test_percent_remaining;
|
||
|
+ gint self_test_polling_short;
|
||
|
+ gint self_test_polling_extended;
|
||
|
+ gint self_test_polling_conveyance;
|
||
|
+ guint smart_capabilities;
|
||
|
+ BDSmartATAAttribute **attributes;
|
||
|
+ guint power_on_time;
|
||
|
+ guint64 power_cycle_count;
|
||
|
+ guint temperature;
|
||
|
+} BDSmartATA;
|
||
|
+
|
||
|
+
|
||
|
+/* TODO: check against some other SCSI implementation for the right wording */
|
||
|
+/**
|
||
|
+ * BDSmartSCSIInformationalException:
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NONE: No SCSI Informational Exception raised.
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ABORTED_COMMAND: Warning - aborted command [asc 0x0b, ascq 0x00].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_TEMPERATURE_EXCEEDED: Warning - specified temperature exceeded [asc 0x0b, ascq 0x01].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ENCLOSURE_DEGRADED: Warning - enclosure degraded [asc 0x0b, ascq 0x02].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SELFTEST_FAILED: Warning - background self-test failed [asc 0x0b, ascq 0x03].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_PRESCAN_MEDIUM_ERROR: Warning - background pre-scan detected medium error [asc 0x0b, ascq 0x04].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SCAN_MEDIUM_ERROR: Warning - background medium scan detected medium error [asc 0x0b, ascq 0x05].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_VOLATILE: Warning - non-volatile cache now volatile [asc 0x0b, ascq 0x06].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_DEGRADED_POWER: Warning - degraded power to non-volatile cache [asc 0x0b, ascq 0x07].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_POWER_LOSS_EXPECTED: Warning - power loss expected [asc 0x0b, ascq 0x08].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_STATISTICS_NOTIFICATION: Warning - device statistics notification active [asc 0x0b, ascq 0x09].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_TEMP: Warning - high critical temperature limit exceeded [asc 0x0b, ascq 0x0a].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_TEMP: Warning - low critical temperature limit exceeded [asc 0x0b, ascq 0x0b].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_TEMP: Warning - high operating temperature limit exceeded [asc 0x0b, ascq 0x0c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_TEMP: Warning - low operating temperature limit exceeded [asc 0x0b, ascq 0x0d].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_HUMIDITY: Warning - high critical humidity limit exceeded [asc 0x0b, ascq 0x0e].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_HUMIDITY: Warning - low critical humidity limit exceeded [asc 0x0b, ascq 0x0f].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_HUMIDITY: Warning - high operating humidity limit exceeded [asc 0x0b, ascq 0x10].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_HUMIDITY: Warning - low operating humidity limit exceeded [asc 0x0b, ascq 0x11].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SECURITY_RISK: Warning - microcode security at risk [asc 0x0b, ascq 0x12].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SIGNATURE_VALIDATION_FAILURE: Warning - microcode digital signature validation failure [asc 0x0b, ascq 0x13].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_PHYSICAL_ELEMENT_STATUS_CHANGE: Warning - physical element status change [asc 0x0b, ascq 0x14].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FAILURE_PREDICTION_THRESH: Failure prediction threshold exceeded [asc 0x5d, ascq 0x00, ascq 0xff].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_FAILURE_PREDICTION_THRESH: Media failure prediction threshold exceeded [asc 0x5d, ascq 0x01].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOGICAL_UNIT_FAILURE_PREDICTION_THRESH: Logical unit failure prediction threshold exceeded [asc 0x5d, ascq 0x02].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPARE_EXHAUSTION_PREDICTION_THRESH: Spare area exhaustion prediction threshold exceeded [asc 0x5d, ascq 0x03].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HARDWARE_IMPENDING_FAILURE: Hardware impending failure [asc 0x5d, ascq 0x10..0x1d].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_CONTROLLER_IMPENDING_FAILURE: Controller impending failure [asc 0x5d, ascq 0x20..0x2c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_DATA_CHANNEL_IMPENDING_FAILURE: Data channel impending failure [asc 0x5d, ascq 0x30..0x3c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SERVO_IMPENDING_FAILURE: Servo impending failure [asc 0x5d, ascq 0x40..0x4c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPINDLE_IMPENDING_FAILURE: Spindle impending failure [asc 0x5d, ascq 0x50..0x5c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FIRMWARE_IMPENDING_FAILURE: Firmware impending failure [asc 0x5d, ascq 0x60..0x6c].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_ENDURANCE_LIMIT: Media impending failure endurance limit met [asc 0x5d, ascq 0x73].
|
||
|
+ * @BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED: Unspecified warning.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NONE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ABORTED_COMMAND,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_TEMPERATURE_EXCEEDED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ENCLOSURE_DEGRADED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SELFTEST_FAILED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_PRESCAN_MEDIUM_ERROR,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SCAN_MEDIUM_ERROR,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_VOLATILE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_DEGRADED_POWER,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_POWER_LOSS_EXPECTED,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_STATISTICS_NOTIFICATION,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_TEMP,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_HUMIDITY,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SECURITY_RISK,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SIGNATURE_VALIDATION_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_PHYSICAL_ELEMENT_STATUS_CHANGE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FAILURE_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_FAILURE_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOGICAL_UNIT_FAILURE_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPARE_EXHAUSTION_PREDICTION_THRESH,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HARDWARE_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_CONTROLLER_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_DATA_CHANNEL_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SERVO_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPINDLE_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FIRMWARE_IMPENDING_FAILURE,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_ENDURANCE_LIMIT,
|
||
|
+ BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED,
|
||
|
+} BDSmartSCSIInformationalException;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartSCSIBackgroundScanStatus:
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_NO_SCANS_ACTIVE: No scans active.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_SCAN_ACTIVE: Scan is active.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_PRESCAN_ACTIVE: Pre-scan is active.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_FATAL: Halted due to fatal error.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_PATTERN_VENDOR_SPECIFIC: Halted due to a vendor specific pattern of error.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_PLIST: Halted due to medium formatted without P-List.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_VENDOR_SPECIFIC: Halted - vendor specific cause.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_TEMPERATURE: Halted due to temperature out of range.
|
||
|
+ * @BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_BMS_TIMER: Waiting until BMS interval timer expires.
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_NO_SCANS_ACTIVE = 0x00,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_SCAN_ACTIVE = 0x01,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_PRESCAN_ACTIVE = 0x02,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_FATAL = 0x03,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_PATTERN_VENDOR_SPECIFIC = 0x04,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_PLIST = 0x05,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_VENDOR_SPECIFIC = 0x06,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_TEMPERATURE = 0x07,
|
||
|
+ BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_BMS_TIMER = 0x08,
|
||
|
+} BDSmartSCSIBackgroundScanStatus;
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartSCSI:
|
||
|
+ * @smart_supported: Indicates that the device has SMART capability.
|
||
|
+ * @smart_enabled: Indicates that the SMART support is enabled.
|
||
|
+ * @overall_status_passed: %TRUE if the device SMART overall-health self-assessment test result has passed, %FALSE otherwise with @scsi_ie fields set.
|
||
|
+ * @scsi_ie: The reported SCSI Informational Exception in a simplified form. See #BDSmartSCSIInformationalException.
|
||
|
+ * @scsi_ie_asc: The reported SCSI Informational Exception ASC (Additional Sense Code) value (only values of 0xb - warnings and 0x5d - impending failures are taken in account).
|
||
|
+ * @scsi_ie_ascq: The reported SCSI Informational Exception ASCQ (Additional Sense Code Qualifier) value.
|
||
|
+ * @scsi_ie_string: String representation of the current SCSI Informational Exception.
|
||
|
+ * @background_scan_status: Background scan status, see #BDSmartSCSIBackgroundScanStatus.
|
||
|
+ * @background_scan_progress: Percent of a background scan progress.
|
||
|
+ * @background_scan_runs: Number of background scans performed.
|
||
|
+ * @background_medium_scan_runs: Number of background medium scans performed.
|
||
|
+ * @read_errors_corrected_eccfast: Error counter log - read errors corrected by ECC fast.
|
||
|
+ * @read_errors_corrected_eccdelayed: Error counter log - read errors corrected by ECC delayed.
|
||
|
+ * @read_errors_corrected_rereads: Error counter log - read errors corrected by rereads.
|
||
|
+ * @read_errors_corrected_total: Error counter log - total read errors corrected.
|
||
|
+ * @read_errors_uncorrected: Error counter log - total uncorrected read errors.
|
||
|
+ * @read_processed_bytes: Error counter log - total bytes processed.
|
||
|
+ * @write_errors_corrected_eccfast: Error counter log - write errors corrected by ECC fast.
|
||
|
+ * @write_errors_corrected_eccdelayed: Error counter log - write errors corrected by ECC delayed.
|
||
|
+ * @write_errors_corrected_rewrites: Error counter log - write errors corrected by rewrites.
|
||
|
+ * @write_errors_corrected_total: Error counter log - total write errors corrected.
|
||
|
+ * @write_errors_uncorrected: Error counter log - total uncorrected write errors.
|
||
|
+ * @write_processed_bytes: Error counter log - total bytes processed.
|
||
|
+ * @start_stop_cycle_count: Accumulated start-stop cycles.
|
||
|
+ * @start_stop_cycle_lifetime: Specified cycle count over device lifetime.
|
||
|
+ * @load_unload_cycle_count: Accumulated load-unload cycles.
|
||
|
+ * @load_unload_cycle_lifetime: Specified load-unload count over device lifetime.
|
||
|
+ * @scsi_grown_defect_list: Elements in grown defect list.
|
||
|
+ * @power_on_time: Accumulated power on time in minutes.
|
||
|
+ * @temperature_warning_enabled: Indicates that temperature warning is enabled.
|
||
|
+ * @temperature: The current drive temperature in Kelvin or 0 when temperature is not reported.
|
||
|
+ * @temperature_drive_trip: The drive trip temperature in Kelvin or 0 when temperature is not reported.
|
||
|
+ */
|
||
|
+typedef struct BDSmartSCSI {
|
||
|
+ gboolean smart_supported;
|
||
|
+ gboolean smart_enabled;
|
||
|
+ gboolean overall_status_passed;
|
||
|
+ BDSmartSCSIInformationalException scsi_ie;
|
||
|
+ guint8 scsi_ie_asc;
|
||
|
+ guint8 scsi_ie_ascq;
|
||
|
+ gchar *scsi_ie_string;
|
||
|
+ BDSmartSCSIBackgroundScanStatus background_scan_status;
|
||
|
+ gdouble background_scan_progress;
|
||
|
+ guint background_scan_runs;
|
||
|
+ guint background_medium_scan_runs;
|
||
|
+ guint read_errors_corrected_eccfast;
|
||
|
+ guint read_errors_corrected_eccdelayed;
|
||
|
+ guint read_errors_corrected_rereads;
|
||
|
+ guint read_errors_corrected_total;
|
||
|
+ guint read_errors_uncorrected;
|
||
|
+ guint64 read_processed_bytes;
|
||
|
+ guint write_errors_corrected_eccfast;
|
||
|
+ guint write_errors_corrected_eccdelayed;
|
||
|
+ guint write_errors_corrected_rewrites;
|
||
|
+ guint write_errors_corrected_total;
|
||
|
+ guint write_errors_uncorrected;
|
||
|
+ guint64 write_processed_bytes;
|
||
|
+ guint start_stop_cycle_count;
|
||
|
+ guint start_stop_cycle_lifetime;
|
||
|
+ guint load_unload_cycle_count;
|
||
|
+ guint load_unload_cycle_lifetime;
|
||
|
+ guint scsi_grown_defect_list;
|
||
|
+ guint power_on_time;
|
||
|
+ gboolean temperature_warning_enabled;
|
||
|
+ guint temperature;
|
||
|
+ guint temperature_drive_trip;
|
||
|
+} BDSmartSCSI;
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * BDSmartSelfTestOp:
|
||
|
+ * @BD_SMART_SELF_TEST_OP_ABORT: Abort a running SMART self-test.
|
||
|
+ * @BD_SMART_SELF_TEST_OP_OFFLINE: SMART Immediate Offline Test in background (ATA) or a default self test in foreground (SCSI).
|
||
|
+ * @BD_SMART_SELF_TEST_OP_SHORT: SMART Short Self Test in background (ATA) or "Background short" self-test (SCSI).
|
||
|
+ * @BD_SMART_SELF_TEST_OP_LONG: SMART Extended Self Test in background (ATA) or "Background long" self-test (SCSI).
|
||
|
+ * @BD_SMART_SELF_TEST_OP_CONVEYANCE: SMART Conveyance Self Test in background (ATA only).
|
||
|
+ */
|
||
|
+typedef enum {
|
||
|
+ BD_SMART_SELF_TEST_OP_ABORT,
|
||
|
+ BD_SMART_SELF_TEST_OP_OFFLINE,
|
||
|
+ BD_SMART_SELF_TEST_OP_SHORT,
|
||
|
+ BD_SMART_SELF_TEST_OP_LONG,
|
||
|
+ BD_SMART_SELF_TEST_OP_CONVEYANCE,
|
||
|
+} BDSmartSelfTestOp;
|
||
|
+
|
||
|
+
|
||
|
+void bd_smart_ata_free (BDSmartATA *data);
|
||
|
+BDSmartATA * bd_smart_ata_copy (BDSmartATA *data);
|
||
|
+
|
||
|
+void bd_smart_ata_attribute_free (BDSmartATAAttribute *attr);
|
||
|
+BDSmartATAAttribute * bd_smart_ata_attribute_copy (BDSmartATAAttribute *attr);
|
||
|
+
|
||
|
+void bd_smart_scsi_free (BDSmartSCSI *data);
|
||
|
+BDSmartSCSI * bd_smart_scsi_copy (BDSmartSCSI *data);
|
||
|
+
|
||
|
+/*
|
||
|
+ * If using the plugin as a standalone library, the following functions should
|
||
|
+ * be called to:
|
||
|
+ *
|
||
|
+ * check_deps() - check plugin's dependencies, returning TRUE if satisfied
|
||
|
+ * init() - initialize the plugin, returning TRUE on success
|
||
|
+ * close() - clean after the plugin at the end or if no longer used
|
||
|
+ *
|
||
|
+ */
|
||
|
+gboolean bd_smart_check_deps (void);
|
||
|
+gboolean bd_smart_init (void);
|
||
|
+void bd_smart_close (void);
|
||
|
+
|
||
|
+gboolean bd_smart_is_tech_avail (BDSmartTech tech, guint64 mode, GError **error);
|
||
|
+
|
||
|
+
|
||
|
+BDSmartATA * bd_smart_ata_get_info (const gchar *device,
|
||
|
+ const BDExtraArg **extra,
|
||
|
+ GError **error);
|
||
|
+BDSmartATA * bd_smart_ata_get_info_from_data (const guint8 *data,
|
||
|
+ gsize data_len,
|
||
|
+ GError **error);
|
||
|
+BDSmartSCSI * bd_smart_scsi_get_info (const gchar *device,
|
||
|
+ const BDExtraArg **extra,
|
||
|
+ GError **error);
|
||
|
+gboolean bd_smart_set_enabled (const gchar *device,
|
||
|
+ gboolean enabled,
|
||
|
+ const BDExtraArg **extra,
|
||
|
+ GError **error);
|
||
|
+gboolean bd_smart_device_self_test (const gchar *device,
|
||
|
+ BDSmartSelfTestOp operation,
|
||
|
+ const BDExtraArg **extra,
|
||
|
+ GError **error);
|
||
|
+
|
||
|
+#endif /* BD_SMART */
|
||
|
diff --git a/src/plugins/smart/smartmontools.c b/src/plugins/smart/smartmontools.c
|
||
|
new file mode 100644
|
||
|
index 00000000..fa28ab71
|
||
|
--- /dev/null
|
||
|
+++ b/src/plugins/smart/smartmontools.c
|
||
|
@@ -0,0 +1,1252 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2014-2023 Red Hat, Inc.
|
||
|
+ *
|
||
|
+ * This library 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.
|
||
|
+ *
|
||
|
+ * This library 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 this library; if not, see <http://www.gnu.org/licenses/>.
|
||
|
+ *
|
||
|
+ * Author: Tomas Bzatek <tbzatek@redhat.com>
|
||
|
+ */
|
||
|
+
|
||
|
+#include <glib.h>
|
||
|
+#include <string.h>
|
||
|
+#include <stdio.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <errno.h>
|
||
|
+#include <inttypes.h>
|
||
|
+
|
||
|
+#include <json-glib/json-glib.h>
|
||
|
+
|
||
|
+#include <blockdev/utils.h>
|
||
|
+#include <check_deps.h>
|
||
|
+
|
||
|
+#include "smart.h"
|
||
|
+#include "smart-private.h"
|
||
|
+
|
||
|
+/**
|
||
|
+ * SECTION: smart
|
||
|
+ * @short_description: SMART device reporting and management.
|
||
|
+ * @title: SMART
|
||
|
+ * @include: smart.h
|
||
|
+ *
|
||
|
+ * A plugin for SMART device reporting and management, based around smartmontools.
|
||
|
+ */
|
||
|
+
|
||
|
+#define SMARTCTL_MIN_VERSION "7.0"
|
||
|
+
|
||
|
+static volatile guint avail_deps = 0;
|
||
|
+static GMutex deps_check_lock;
|
||
|
+
|
||
|
+#define DEPS_SMART 0
|
||
|
+#define DEPS_SMART_MASK (1 << DEPS_SMART)
|
||
|
+#define DEPS_LAST 1
|
||
|
+
|
||
|
+static const UtilDep deps[DEPS_LAST] = {
|
||
|
+ { "smartctl", SMARTCTL_MIN_VERSION, NULL, "smartctl ([\\d\\.]+) .*" },
|
||
|
+};
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_check_deps:
|
||
|
+ *
|
||
|
+ * Returns: whether the plugin's runtime dependencies are satisfied or not
|
||
|
+ *
|
||
|
+ * Function checking plugin's runtime dependencies.
|
||
|
+ */
|
||
|
+gboolean bd_smart_check_deps (void) {
|
||
|
+ GError *error = NULL;
|
||
|
+ guint i = 0;
|
||
|
+ gboolean status = FALSE;
|
||
|
+ gboolean ret = TRUE;
|
||
|
+
|
||
|
+ for (i = 0; i < DEPS_LAST; i++) {
|
||
|
+ status = bd_utils_check_util_version (deps[i].name, deps[i].version,
|
||
|
+ deps[i].ver_arg, deps[i].ver_regexp, &error);
|
||
|
+ if (!status)
|
||
|
+ bd_utils_log_format (BD_UTILS_LOG_WARNING, "%s", error->message);
|
||
|
+ else
|
||
|
+ g_atomic_int_or (&avail_deps, 1 << i);
|
||
|
+ g_clear_error (&error);
|
||
|
+ ret = ret && status;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!ret)
|
||
|
+ bd_utils_log_format (BD_UTILS_LOG_WARNING, "Cannot load the SMART plugin");
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_is_tech_avail:
|
||
|
+ * @tech: the queried tech
|
||
|
+ * @mode: a bit mask of queried modes of operation (#BDSmartTechMode) for @tech
|
||
|
+ * @error: (out) (nullable): place to store error (details about why the @tech-@mode combination is not available)
|
||
|
+ *
|
||
|
+ * Returns: whether the @tech-@mode combination is available -- supported by the
|
||
|
+ * plugin implementation and having all the runtime dependencies available
|
||
|
+ */
|
||
|
+gboolean bd_smart_is_tech_avail (G_GNUC_UNUSED BDSmartTech tech, G_GNUC_UNUSED guint64 mode, GError **error) {
|
||
|
+ /* all tech-mode combinations are supported by this implementation of the plugin */
|
||
|
+ return check_deps (&avail_deps, DEPS_SMART_MASK, deps, DEPS_LAST, &deps_check_lock, error);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+static const gchar * get_error_message_from_exit_code (gint exit_code) {
|
||
|
+ /*
|
||
|
+ * bit 0: Command line did not parse.
|
||
|
+ * bit 1: Device open failed, device did not return an IDENTIFY DEVICE structure, or device is in a low-power mode
|
||
|
+ * bit 2: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure
|
||
|
+ */
|
||
|
+
|
||
|
+ if (exit_code & 0x01)
|
||
|
+ return "Command line did not parse.";
|
||
|
+ if (exit_code & 0x02)
|
||
|
+ return "Device open failed or device did not return an IDENTIFY DEVICE structure.";
|
||
|
+ if (exit_code & 0x04)
|
||
|
+ return "Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure.";
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static void lookup_well_known_attr (BDSmartATAAttribute *a,
|
||
|
+ gchar **well_known_name,
|
||
|
+ gint64 *pretty_value,
|
||
|
+ BDSmartATAAttributeUnit *pretty_unit) {
|
||
|
+ *well_known_name = g_strdup (well_known_attrs[a->id].libatasmart_name);
|
||
|
+ if (*well_known_name) {
|
||
|
+ /* verify matching attribute names */
|
||
|
+ const gchar * const *n;
|
||
|
+ gboolean trusted = FALSE;
|
||
|
+
|
||
|
+ for (n = well_known_attrs[a->id].smartmontools_names; *n; n++)
|
||
|
+ if (g_strcmp0 (*n, a->name) == 0) {
|
||
|
+ trusted = TRUE;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ if (trusted) {
|
||
|
+ char *endptr = NULL;
|
||
|
+ guint64 hour, min, sec, usec;
|
||
|
+
|
||
|
+ *pretty_unit = well_known_attrs[a->id].unit;
|
||
|
+ switch (well_known_attrs[a->id].unit) {
|
||
|
+ /* FIXME: we have the 64-bit raw value but no context how it's supposed
|
||
|
+ * to be represented. This is defined in the smartmontools drivedb
|
||
|
+ * yet not exposed over to JSON.
|
||
|
+ */
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN:
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_NONE:
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS:
|
||
|
+ /* try converting whatever possible and simply ignore the rest */
|
||
|
+ *pretty_value = g_ascii_strtoll (a->pretty_value_string, &endptr, 0);
|
||
|
+ if (! endptr || endptr == a->pretty_value_string)
|
||
|
+ *pretty_value = a->value_raw;
|
||
|
+ break;
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS:
|
||
|
+ /* possible formats as printed by ata_format_attr_raw_value():
|
||
|
+ * "%lluh+%02llum (%u)"
|
||
|
+ * "%lluh+%02llum+%02llus"
|
||
|
+ * "%lluh+%02llum"
|
||
|
+ * "%uh+%02um+%02u.%03us"
|
||
|
+ */
|
||
|
+ hour = min = sec = usec = 0;
|
||
|
+ if (sscanf (a->pretty_value_string, "%" PRIu64 "h+%" PRIu64 "m+%" PRIu64 "s.%" PRIu64 "s", &hour, &min, &sec, &usec) >= 2)
|
||
|
+ *pretty_value = ((hour * 60 + min) * 60 + sec) * 1000 + usec;
|
||
|
+ else
|
||
|
+ *pretty_value = a->value_raw;
|
||
|
+ break;
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN:
|
||
|
+ /* possible formats as printed by ata_format_attr_raw_value():
|
||
|
+ * "%d"
|
||
|
+ * "%d (Min/Max %d/%d)"
|
||
|
+ * "%d (Min/Max %d/%d #%d)"
|
||
|
+ * "%d (%d %d %d %d %d)"
|
||
|
+ * "%d.%d"
|
||
|
+ */
|
||
|
+ *pretty_value = g_ascii_strtoll (a->pretty_value_string, &endptr, 0);
|
||
|
+ if (! endptr || endptr == a->pretty_value_string)
|
||
|
+ *pretty_value = a->value_raw;
|
||
|
+ else
|
||
|
+ /* temperature in degrees Celsius, need millikelvins */
|
||
|
+ *pretty_value = *pretty_value * 1000 + 273150;
|
||
|
+ break;
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT:
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT:
|
||
|
+ case BD_SMART_ATA_ATTRIBUTE_UNIT_MB:
|
||
|
+ default:
|
||
|
+ /* not implemented */
|
||
|
+ *pretty_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ g_free (*well_known_name);
|
||
|
+ *well_known_name = NULL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (*well_known_name == NULL) {
|
||
|
+ /* not a well-known attribute or failed verification */
|
||
|
+ *pretty_unit = 0;
|
||
|
+ *pretty_value = a->value_raw;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/* Returns num elements read or -1 in case of an error. */
|
||
|
+static gint parse_int_array (JsonReader *reader, const gchar *key, gint64 *dest, gint max_count, GError **error) {
|
||
|
+ gint count;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (! json_reader_read_member (reader, key)) {
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ json_reader_get_error (reader)->message);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ count = MIN (max_count, json_reader_count_elements (reader));
|
||
|
+ if (count < 0) {
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ json_reader_get_error (reader)->message);
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ for (i = 0; i < count; i++) {
|
||
|
+ if (! json_reader_read_element (reader, i)) {
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ json_reader_get_error (reader)->message);
|
||
|
+ json_reader_end_element (reader);
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ dest[i] = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_element (reader);
|
||
|
+ }
|
||
|
+
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ return count;
|
||
|
+}
|
||
|
+
|
||
|
+/* Returns null-terminated list of messages marked as severity=error */
|
||
|
+static gchar ** parse_error_messages (JsonReader *reader) {
|
||
|
+ GPtrArray *a;
|
||
|
+ gint count;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (! json_reader_read_member (reader, "smartctl")) {
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ if (! json_reader_read_member (reader, "messages")) {
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ a = g_ptr_array_new_full (0, g_free);
|
||
|
+ count = json_reader_count_elements (reader);
|
||
|
+
|
||
|
+ for (i = 0; count >= 0 && i < count; i++) {
|
||
|
+ if (! json_reader_read_element (reader, i)) {
|
||
|
+ json_reader_end_element (reader);
|
||
|
+ g_ptr_array_free (a, TRUE);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ if (json_reader_is_object (reader)) {
|
||
|
+ gboolean severity_error = FALSE;
|
||
|
+
|
||
|
+ if (json_reader_read_member (reader, "severity"))
|
||
|
+ severity_error = g_strcmp0 ("error", json_reader_get_string_value (reader)) == 0;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ if (severity_error) {
|
||
|
+ if (json_reader_read_member (reader, "string")) {
|
||
|
+ const gchar *val = json_reader_get_string_value (reader);
|
||
|
+ if (val)
|
||
|
+ g_ptr_array_add (a, g_strdup (val));
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ json_reader_end_element (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ g_ptr_array_add (a, NULL);
|
||
|
+ return (gchar **) g_ptr_array_free (a, FALSE);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+#define MIN_JSON_FORMAT_VER 1 /* minimal json_format_version */
|
||
|
+
|
||
|
+static gboolean parse_smartctl_error (gint status, const gchar *stdout, const gchar *stderr, JsonParser *parser, GError **error) {
|
||
|
+ gint res;
|
||
|
+ JsonReader *reader;
|
||
|
+ gint64 ver_info[2] = { 0, 0 };
|
||
|
+ GError *l_error = NULL;
|
||
|
+
|
||
|
+ if ((!stdout || strlen (stdout) == 0) &&
|
||
|
+ (!stderr || strlen (stderr) == 0)) {
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ (status & 0x07) ? get_error_message_from_exit_code (status) : "Empty response");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+ /* Expecting proper JSON output on stdout, take what has been received on stderr */
|
||
|
+ if (!stdout || strlen (stdout) == 0) {
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ stderr);
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Parse the JSON output */
|
||
|
+ if (! json_parser_load_from_data (parser, stdout, -1, &l_error) ||
|
||
|
+ ! json_parser_get_root (parser)) {
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ l_error->message);
|
||
|
+ g_error_free (l_error);
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+ reader = json_reader_new (json_parser_get_root (parser));
|
||
|
+
|
||
|
+ /* Verify the JSON output format */
|
||
|
+ res = parse_int_array (reader, "json_format_version", ver_info, G_N_ELEMENTS (ver_info), error);
|
||
|
+ if (res < 1) {
|
||
|
+ g_prefix_error (error, "Error parsing version info: ");
|
||
|
+ g_object_unref (reader);
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+ if (ver_info[0] < MIN_JSON_FORMAT_VER) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ "Reported smartctl JSON format version too low: %" G_GUINT64_FORMAT " (required: %d)",
|
||
|
+ ver_info[0], MIN_JSON_FORMAT_VER);
|
||
|
+ g_object_unref (reader);
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+ if (ver_info[0] > MIN_JSON_FORMAT_VER)
|
||
|
+ g_warning ("Reported smartctl JSON format major version higher than expected, expect parse issues");
|
||
|
+
|
||
|
+ /* Find out the return code and associated messages */
|
||
|
+ if (status & 0x07) {
|
||
|
+ gchar **messages;
|
||
|
+
|
||
|
+ messages = parse_error_messages (reader);
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
|
||
|
+ messages && messages[0] ? messages[0] : get_error_message_from_exit_code (status));
|
||
|
+ g_strfreev (messages);
|
||
|
+ g_object_unref (reader);
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ g_object_unref (reader);
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+static BDSmartATAAttribute ** parse_ata_smart_attributes (JsonReader *reader, GError **error) {
|
||
|
+ GPtrArray *ptr_array;
|
||
|
+ gint count;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ ptr_array = g_ptr_array_new_full (0, (GDestroyNotify) bd_smart_ata_attribute_free);
|
||
|
+ count = json_reader_count_elements (reader);
|
||
|
+ for (i = 0; count > 0 && i < count; i++) {
|
||
|
+ BDSmartATAAttribute *attr;
|
||
|
+ gint64 f;
|
||
|
+
|
||
|
+ if (! json_reader_read_element (reader, i)) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ "Error parsing smartctl JSON ata_smart_attributes[%d] element: %s",
|
||
|
+ i, json_reader_get_error (reader)->message);
|
||
|
+ g_ptr_array_free (ptr_array, TRUE);
|
||
|
+ json_reader_end_element (reader);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ attr = g_new0 (BDSmartATAAttribute, 1);
|
||
|
+
|
||
|
+#define _READ_AND_CHECK(elem_name) \
|
||
|
+ if (! json_reader_read_member (reader, elem_name)) { \
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT, \
|
||
|
+ "Error parsing smartctl JSON ata_smart_attributes[%d] element: %s", \
|
||
|
+ i, json_reader_get_error (reader)->message); \
|
||
|
+ g_ptr_array_free (ptr_array, TRUE); \
|
||
|
+ bd_smart_ata_attribute_free (attr); \
|
||
|
+ json_reader_end_member (reader); \
|
||
|
+ json_reader_end_element (reader); \
|
||
|
+ return NULL; \
|
||
|
+ }
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("id");
|
||
|
+ attr->id = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("name");
|
||
|
+ attr->name = g_strdup (json_reader_get_string_value (reader));
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("value");
|
||
|
+ attr->value = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("worst");
|
||
|
+ attr->worst = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("thresh");
|
||
|
+ attr->threshold = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("when_failed");
|
||
|
+ if (g_strcmp0 (json_reader_get_string_value (reader), "past") == 0)
|
||
|
+ attr->failed_past = TRUE;
|
||
|
+ else
|
||
|
+ if (g_strcmp0 (json_reader_get_string_value (reader), "now") == 0)
|
||
|
+ attr->failing_now = TRUE;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("raw");
|
||
|
+ _READ_AND_CHECK ("value");
|
||
|
+ attr->value_raw = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ _READ_AND_CHECK ("string");
|
||
|
+ attr->pretty_value_string = g_strdup (json_reader_get_string_value (reader));
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ _READ_AND_CHECK ("flags");
|
||
|
+ _READ_AND_CHECK ("value");
|
||
|
+ f = json_reader_get_int_value (reader);
|
||
|
+ if (f & 0x01)
|
||
|
+ attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_PREFAILURE;
|
||
|
+ if (f & 0x02)
|
||
|
+ attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_ONLINE;
|
||
|
+ if (f & 0x04)
|
||
|
+ attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_PERFORMANCE;
|
||
|
+ if (f & 0x08)
|
||
|
+ attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_ERROR_RATE;
|
||
|
+ if (f & 0x10)
|
||
|
+ attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_EVENT_COUNT;
|
||
|
+ if (f & 0x20)
|
||
|
+ attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_SELF_PRESERVING;
|
||
|
+ if (f & 0xffc0)
|
||
|
+ attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_OTHER;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ json_reader_end_element (reader);
|
||
|
+
|
||
|
+#undef _READ_AND_CHECK
|
||
|
+
|
||
|
+ lookup_well_known_attr (attr,
|
||
|
+ &attr->well_known_name,
|
||
|
+ &attr->pretty_value,
|
||
|
+ &attr->pretty_value_unit);
|
||
|
+ g_ptr_array_add (ptr_array, attr);
|
||
|
+ }
|
||
|
+
|
||
|
+ g_ptr_array_add (ptr_array, NULL);
|
||
|
+ return (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);
|
||
|
+}
|
||
|
+
|
||
|
+static BDSmartATA * parse_ata_smart (JsonParser *parser, GError **error) {
|
||
|
+ BDSmartATA *data;
|
||
|
+ JsonReader *reader;
|
||
|
+
|
||
|
+ data = g_new0 (BDSmartATA, 1);
|
||
|
+ reader = json_reader_new (json_parser_get_root (parser));
|
||
|
+
|
||
|
+ /* smart_support section */
|
||
|
+ if (json_reader_read_member (reader, "smart_support")) {
|
||
|
+ if (json_reader_read_member (reader, "available"))
|
||
|
+ data->smart_supported = json_reader_get_boolean_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "enabled"))
|
||
|
+ data->smart_enabled = json_reader_get_boolean_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* smart_status section */
|
||
|
+ if (json_reader_read_member (reader, "smart_status")) {
|
||
|
+ if (json_reader_read_member (reader, "passed"))
|
||
|
+ data->overall_status_passed = json_reader_get_boolean_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* ata_smart_data section */
|
||
|
+ if (! json_reader_read_member (reader, "ata_smart_data")) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ "Error parsing smartctl JSON data: %s",
|
||
|
+ json_reader_get_error (reader)->message);
|
||
|
+ g_object_unref (reader);
|
||
|
+ bd_smart_ata_free (data);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ if (json_reader_read_member (reader, "offline_data_collection")) {
|
||
|
+ if (json_reader_read_member (reader, "status")) {
|
||
|
+ if (json_reader_read_member (reader, "value")) {
|
||
|
+ gint64 val = json_reader_get_int_value (reader);
|
||
|
+
|
||
|
+ switch (val & 0x7f) {
|
||
|
+ case 0x00:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED;
|
||
|
+ break;
|
||
|
+ case 0x02:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR;
|
||
|
+ break;
|
||
|
+ case 0x03:
|
||
|
+ if (val == 0x03)
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS;
|
||
|
+ else
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED;
|
||
|
+ break;
|
||
|
+ case 0x04:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR;
|
||
|
+ break;
|
||
|
+ case 0x05:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR;
|
||
|
+ break;
|
||
|
+ case 0x06:
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ if ((val & 0x7f) >= 0x40)
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC;
|
||
|
+ else
|
||
|
+ data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ data->auto_offline_data_collection_enabled = val & 0x80;
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "completion_seconds"))
|
||
|
+ data->offline_data_collection_completion = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader); /* offline_data_collection */
|
||
|
+
|
||
|
+ if (json_reader_read_member (reader, "self_test")) {
|
||
|
+ if (json_reader_read_member (reader, "status")) {
|
||
|
+ if (json_reader_read_member (reader, "value")) {
|
||
|
+ gint64 val = json_reader_get_int_value (reader);
|
||
|
+
|
||
|
+ switch (val >> 4) {
|
||
|
+ case 0x00:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR;
|
||
|
+ break;
|
||
|
+ case 0x01:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST;
|
||
|
+ break;
|
||
|
+ case 0x02:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET;
|
||
|
+ break;
|
||
|
+ case 0x03:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_FATAL;
|
||
|
+ break;
|
||
|
+ case 0x04:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN;
|
||
|
+ break;
|
||
|
+ case 0x05:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL;
|
||
|
+ break;
|
||
|
+ case 0x06:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO;
|
||
|
+ break;
|
||
|
+ case 0x07:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ;
|
||
|
+ break;
|
||
|
+ case 0x08:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING;
|
||
|
+ break;
|
||
|
+ case 0x0f:
|
||
|
+ data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS;
|
||
|
+ data->self_test_percent_remaining = (val & 0x0f) * 10;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "polling_minutes")) {
|
||
|
+ if (json_reader_read_member (reader, "short"))
|
||
|
+ data->self_test_polling_short = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "extended"))
|
||
|
+ data->self_test_polling_extended = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "conveyance"))
|
||
|
+ data->self_test_polling_conveyance = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader); /* self_test */
|
||
|
+
|
||
|
+ if (json_reader_read_member (reader, "capabilities")) {
|
||
|
+ gint64 val[2] = { 0, 0 };
|
||
|
+
|
||
|
+ if (parse_int_array (reader, "values", val, G_N_ELEMENTS (val), NULL) == G_N_ELEMENTS (val)) {
|
||
|
+ if (val[0] == 0x00)
|
||
|
+ data->offline_data_collection_capabilities = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_NOT_SUPPORTED;
|
||
|
+ else {
|
||
|
+ if (val[0] & 0x01)
|
||
|
+ data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_EXEC_OFFLINE_IMMEDIATE;
|
||
|
+ /* 0x02 is deprecated - SupportAutomaticTimer */
|
||
|
+ if (val[0] & 0x04)
|
||
|
+ data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_ABORT;
|
||
|
+ if (val[0] & 0x08)
|
||
|
+ data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_SURFACE_SCAN;
|
||
|
+ if (val[0] & 0x10)
|
||
|
+ data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELF_TEST;
|
||
|
+ if (val[0] & 0x20)
|
||
|
+ data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_CONVEYANCE_SELF_TEST;
|
||
|
+ if (val[0] & 0x40)
|
||
|
+ data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELECTIVE_SELF_TEST;
|
||
|
+ }
|
||
|
+ if (val[1] & 0x01)
|
||
|
+ data->smart_capabilities |= BD_SMART_ATA_CAP_ATTRIBUTE_AUTOSAVE;
|
||
|
+ if (val[1] & 0x02)
|
||
|
+ data->smart_capabilities |= BD_SMART_ATA_CAP_AUTOSAVE_TIMER;
|
||
|
+ }
|
||
|
+ if (json_reader_read_member (reader, "error_logging_supported"))
|
||
|
+ if (json_reader_get_boolean_value (reader))
|
||
|
+ data->smart_capabilities |= BD_SMART_ATA_CAP_ERROR_LOGGING;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "gp_logging_supported"))
|
||
|
+ if (json_reader_get_boolean_value (reader))
|
||
|
+ data->smart_capabilities |= BD_SMART_ATA_CAP_GP_LOGGING;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader); /* capabilities */
|
||
|
+ json_reader_end_member (reader); /* ata_smart_data */
|
||
|
+
|
||
|
+ /* ata_smart_attributes section */
|
||
|
+ if (! json_reader_read_member (reader, "ata_smart_attributes") ||
|
||
|
+ ! json_reader_read_member (reader, "table") ||
|
||
|
+ ! json_reader_is_array (reader)) {
|
||
|
+ g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ "Error parsing smartctl JSON data: %s",
|
||
|
+ json_reader_get_error (reader)->message);
|
||
|
+ g_object_unref (reader);
|
||
|
+ bd_smart_ata_free (data);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ data->attributes = parse_ata_smart_attributes (reader, error);
|
||
|
+ if (! data->attributes) {
|
||
|
+ g_object_unref (reader);
|
||
|
+ bd_smart_ata_free (data);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader); /* table */
|
||
|
+ json_reader_end_member (reader); /* ata_smart_attributes */
|
||
|
+
|
||
|
+ /* power_on_time section */
|
||
|
+ if (json_reader_read_member (reader, "power_on_time")) {
|
||
|
+ if (json_reader_read_member (reader, "hours"))
|
||
|
+ data->power_on_time += json_reader_get_int_value (reader) * 60;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "minutes"))
|
||
|
+ data->power_on_time += json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* power_cycle_count section */
|
||
|
+ if (json_reader_read_member (reader, "power_cycle_count"))
|
||
|
+ data->power_cycle_count = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* temperature section */
|
||
|
+ if (json_reader_read_member (reader, "temperature")) {
|
||
|
+ if (json_reader_read_member (reader, "current"))
|
||
|
+ data->temperature = json_reader_get_int_value (reader) + 273;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ g_object_unref (reader);
|
||
|
+ return data;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static BDSmartSCSI * parse_scsi_smart (JsonParser *parser, G_GNUC_UNUSED GError **error) {
|
||
|
+ BDSmartSCSI *data;
|
||
|
+ JsonReader *reader;
|
||
|
+
|
||
|
+ data = g_new0 (BDSmartSCSI, 1);
|
||
|
+ reader = json_reader_new (json_parser_get_root (parser));
|
||
|
+
|
||
|
+ /* smart_support section */
|
||
|
+ if (json_reader_read_member (reader, "smart_support")) {
|
||
|
+ if (json_reader_read_member (reader, "available"))
|
||
|
+ data->smart_supported = json_reader_get_boolean_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "enabled"))
|
||
|
+ data->smart_enabled = json_reader_get_boolean_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* smart_status section */
|
||
|
+ if (json_reader_read_member (reader, "smart_status")) {
|
||
|
+ if (json_reader_read_member (reader, "passed"))
|
||
|
+ data->overall_status_passed = json_reader_get_boolean_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "scsi")) {
|
||
|
+ gint64 asc = -1;
|
||
|
+ gint64 ascq = -1;
|
||
|
+
|
||
|
+ if (json_reader_read_member (reader, "asc")) {
|
||
|
+ asc = json_reader_get_int_value (reader);
|
||
|
+ data->scsi_ie_asc = asc;
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "ascq")) {
|
||
|
+ ascq = json_reader_get_int_value (reader);
|
||
|
+ data->scsi_ie_ascq = ascq;
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "ie_string"))
|
||
|
+ data->scsi_ie_string = g_strdup (json_reader_get_string_value (reader));
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ if (asc == 0xb && ascq >= 0) {
|
||
|
+ switch (ascq) {
|
||
|
+ case 0x00:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ABORTED_COMMAND;
|
||
|
+ break;
|
||
|
+ case 0x01:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_TEMPERATURE_EXCEEDED;
|
||
|
+ break;
|
||
|
+ case 0x02:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ENCLOSURE_DEGRADED;
|
||
|
+ break;
|
||
|
+ case 0x03:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SELFTEST_FAILED;
|
||
|
+ break;
|
||
|
+ case 0x04:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_PRESCAN_MEDIUM_ERROR;
|
||
|
+ break;
|
||
|
+ case 0x05:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SCAN_MEDIUM_ERROR;
|
||
|
+ break;
|
||
|
+ case 0x06:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_VOLATILE;
|
||
|
+ break;
|
||
|
+ case 0x07:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_DEGRADED_POWER;
|
||
|
+ break;
|
||
|
+ case 0x08:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_POWER_LOSS_EXPECTED;
|
||
|
+ break;
|
||
|
+ case 0x09:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_STATISTICS_NOTIFICATION;
|
||
|
+ break;
|
||
|
+ case 0x0a:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_TEMP;
|
||
|
+ break;
|
||
|
+ case 0x0b:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_TEMP;
|
||
|
+ break;
|
||
|
+ case 0x0c:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_TEMP;
|
||
|
+ break;
|
||
|
+ case 0x0d:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_TEMP;
|
||
|
+ break;
|
||
|
+ case 0x0e:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_HUMIDITY;
|
||
|
+ break;
|
||
|
+ case 0x0f:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_HUMIDITY;
|
||
|
+ break;
|
||
|
+ case 0x10:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_HUMIDITY;
|
||
|
+ break;
|
||
|
+ case 0x11:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_HUMIDITY;
|
||
|
+ break;
|
||
|
+ case 0x12:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SECURITY_RISK;
|
||
|
+ break;
|
||
|
+ case 0x13:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SIGNATURE_VALIDATION_FAILURE;
|
||
|
+ break;
|
||
|
+ case 0x14:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_PHYSICAL_ELEMENT_STATUS_CHANGE;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ } else if (asc == 0x5d && ascq >= 0) {
|
||
|
+ switch (ascq) {
|
||
|
+ case 0x00:
|
||
|
+ case 0xff:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FAILURE_PREDICTION_THRESH;
|
||
|
+ break;
|
||
|
+ case 0x01:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_FAILURE_PREDICTION_THRESH;
|
||
|
+ break;
|
||
|
+ case 0x02:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOGICAL_UNIT_FAILURE_PREDICTION_THRESH;
|
||
|
+ break;
|
||
|
+ case 0x03:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPARE_EXHAUSTION_PREDICTION_THRESH;
|
||
|
+ break;
|
||
|
+ case 0x73:
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_ENDURANCE_LIMIT;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ if (ascq >= 0x10 && ascq <= 0x1d)
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HARDWARE_IMPENDING_FAILURE;
|
||
|
+ else if (ascq >= 0x20 && ascq <= 0x2c)
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_CONTROLLER_IMPENDING_FAILURE;
|
||
|
+ else if (ascq >= 0x30 && ascq <= 0x3c)
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_DATA_CHANNEL_IMPENDING_FAILURE;
|
||
|
+ else if (ascq >= 0x40 && ascq <= 0x4c)
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SERVO_IMPENDING_FAILURE;
|
||
|
+ else if (ascq >= 0x50 && ascq <= 0x5c)
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPINDLE_IMPENDING_FAILURE;
|
||
|
+ else if (ascq >= 0x60 && ascq <= 0x6c)
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FIRMWARE_IMPENDING_FAILURE;
|
||
|
+ else
|
||
|
+ data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* temperature_warning section */
|
||
|
+ if (json_reader_read_member (reader, "temperature_warning")) {
|
||
|
+ if (json_reader_read_member (reader, "enabled"))
|
||
|
+ data->temperature_warning_enabled = json_reader_get_boolean_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* temperature section */
|
||
|
+ if (json_reader_read_member (reader, "temperature")) {
|
||
|
+ if (json_reader_read_member (reader, "current"))
|
||
|
+ data->temperature = json_reader_get_int_value (reader) + 273;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "drive_trip"))
|
||
|
+ data->temperature_drive_trip = json_reader_get_int_value (reader) + 273;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* scsi_background_scan section */
|
||
|
+ if (json_reader_read_member (reader, "scsi_background_scan")) {
|
||
|
+ if (json_reader_read_member (reader, "status")) {
|
||
|
+ if (json_reader_read_member (reader, "value")) {
|
||
|
+ guint64 val = json_reader_get_int_value (reader);
|
||
|
+
|
||
|
+ switch (val) {
|
||
|
+ case 0x00:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_NO_SCANS_ACTIVE;
|
||
|
+ break;
|
||
|
+ case 0x01:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_SCAN_ACTIVE;
|
||
|
+ break;
|
||
|
+ case 0x02:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_PRESCAN_ACTIVE;
|
||
|
+ break;
|
||
|
+ case 0x03:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_FATAL;
|
||
|
+ break;
|
||
|
+ case 0x04:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_PATTERN_VENDOR_SPECIFIC;
|
||
|
+ break;
|
||
|
+ case 0x05:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_PLIST;
|
||
|
+ break;
|
||
|
+ case 0x06:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_VENDOR_SPECIFIC;
|
||
|
+ break;
|
||
|
+ case 0x07:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_TEMPERATURE;
|
||
|
+ break;
|
||
|
+ case 0x08:
|
||
|
+ data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_BMS_TIMER;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ /* just copy the value, it corresponds to the above anyway */
|
||
|
+ data->background_scan_status = val;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "scan_progress")) {
|
||
|
+ const gchar *val = json_reader_get_string_value (reader);
|
||
|
+ float d = 0.0;
|
||
|
+
|
||
|
+ if (sscanf (val, "%f%%", &d) == 1)
|
||
|
+ data->background_scan_progress = d;
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "number_scans_performed"))
|
||
|
+ data->background_scan_runs = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "number_medium_scans_performed"))
|
||
|
+ data->background_medium_scan_runs = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* scsi_start_stop_cycle_counter section */
|
||
|
+ if (json_reader_read_member (reader, "scsi_start_stop_cycle_counter")) {
|
||
|
+ if (json_reader_read_member (reader, "specified_cycle_count_over_device_lifetime"))
|
||
|
+ data->start_stop_cycle_lifetime = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "accumulated_start_stop_cycles"))
|
||
|
+ data->start_stop_cycle_count = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "specified_load_unload_count_over_device_lifetime"))
|
||
|
+ data->load_unload_cycle_lifetime = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "accumulated_load_unload_cycles"))
|
||
|
+ data->load_unload_cycle_count = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* scsi_grown_defect_list section */
|
||
|
+ if (json_reader_read_member (reader, "scsi_grown_defect_list"))
|
||
|
+ data->scsi_grown_defect_list = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* scsi_error_counter_log section */
|
||
|
+ if (json_reader_read_member (reader, "scsi_error_counter_log")) {
|
||
|
+ if (json_reader_read_member (reader, "read")) {
|
||
|
+ if (json_reader_read_member (reader, "errors_corrected_by_eccfast"))
|
||
|
+ data->read_errors_corrected_eccfast = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "errors_corrected_by_eccdelayed"))
|
||
|
+ data->read_errors_corrected_eccdelayed = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "errors_corrected_by_rereads_rewrites"))
|
||
|
+ data->read_errors_corrected_rereads = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "total_errors_corrected"))
|
||
|
+ data->read_errors_corrected_total = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "total_uncorrected_errors"))
|
||
|
+ data->read_errors_uncorrected = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "gigabytes_processed")) {
|
||
|
+ const gchar *val = json_reader_get_string_value (reader);
|
||
|
+ gdouble d = 0.0;
|
||
|
+
|
||
|
+ if (val)
|
||
|
+ d = g_ascii_strtod (val, NULL);
|
||
|
+ data->read_processed_bytes = d * 1000000000;
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "write")) {
|
||
|
+ if (json_reader_read_member (reader, "errors_corrected_by_eccfast"))
|
||
|
+ data->write_errors_corrected_eccfast = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "errors_corrected_by_eccdelayed"))
|
||
|
+ data->write_errors_corrected_eccdelayed = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "errors_corrected_by_rereads_rewrites"))
|
||
|
+ data->write_errors_corrected_rewrites = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "total_errors_corrected"))
|
||
|
+ data->write_errors_corrected_total = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "total_uncorrected_errors"))
|
||
|
+ data->write_errors_uncorrected = json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "gigabytes_processed")) {
|
||
|
+ const gchar *val = json_reader_get_string_value (reader);
|
||
|
+ gdouble d = 0.0;
|
||
|
+
|
||
|
+ if (val)
|
||
|
+ d = g_ascii_strtod (val, NULL);
|
||
|
+ data->write_processed_bytes = d * 1000000000;
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ /* power_on_time section */
|
||
|
+ if (json_reader_read_member (reader, "power_on_time")) {
|
||
|
+ if (json_reader_read_member (reader, "hours"))
|
||
|
+ data->power_on_time += json_reader_get_int_value (reader) * 60;
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ if (json_reader_read_member (reader, "minutes"))
|
||
|
+ data->power_on_time += json_reader_get_int_value (reader);
|
||
|
+ json_reader_end_member (reader);
|
||
|
+ }
|
||
|
+ json_reader_end_member (reader);
|
||
|
+
|
||
|
+ g_object_unref (reader);
|
||
|
+ return data;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_get_info:
|
||
|
+ * @device: device to check.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from the drive.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_get_info (const gchar *device, const BDExtraArg **extra, GError **error) {
|
||
|
+ const gchar *args[8] = { "smartctl", "--info", "--health", "--capabilities", "--attributes", "--json", device, NULL };
|
||
|
+ gint status = 0;
|
||
|
+ gchar *stdout = NULL;
|
||
|
+ gchar *stderr = NULL;
|
||
|
+ JsonParser *parser;
|
||
|
+ BDSmartATA *data = NULL;
|
||
|
+ gboolean ret;
|
||
|
+
|
||
|
+ if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
|
||
|
+ g_prefix_error (error, "Error getting ATA SMART info: ");
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (stdout)
|
||
|
+ g_strstrip (stdout);
|
||
|
+ if (stderr)
|
||
|
+ g_strstrip (stderr);
|
||
|
+
|
||
|
+ parser = json_parser_new ();
|
||
|
+ ret = parse_smartctl_error (status, stdout, stderr, parser, error);
|
||
|
+ g_free (stdout);
|
||
|
+ g_free (stderr);
|
||
|
+ if (! ret) {
|
||
|
+ g_prefix_error (error, "Error getting ATA SMART info: ");
|
||
|
+ g_object_unref (parser);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ data = parse_ata_smart (parser, error);
|
||
|
+ g_object_unref (parser);
|
||
|
+
|
||
|
+ return data;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_ata_get_info_from_data:
|
||
|
+ * @data: (array length=data_len): binary data to parse.
|
||
|
+ * @data_len: length of the data supplied.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from the supplied data.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartATA * bd_smart_ata_get_info_from_data (const guint8 *data, gsize data_len, GError **error) {
|
||
|
+ JsonParser *parser;
|
||
|
+ gchar *stdout;
|
||
|
+ BDSmartATA *ata_data = NULL;
|
||
|
+ gboolean ret;
|
||
|
+
|
||
|
+ g_warn_if_fail (data != NULL);
|
||
|
+ g_warn_if_fail (data_len > 0);
|
||
|
+
|
||
|
+ stdout = g_strndup ((gchar *)data, data_len);
|
||
|
+ g_strstrip (stdout);
|
||
|
+
|
||
|
+ parser = json_parser_new ();
|
||
|
+ ret = parse_smartctl_error (0, stdout, NULL, parser, error);
|
||
|
+ g_free (stdout);
|
||
|
+ if (! ret) {
|
||
|
+ g_prefix_error (error, "Error getting ATA SMART info: ");
|
||
|
+ g_object_unref (parser);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ ata_data = parse_ata_smart (parser, error);
|
||
|
+ g_object_unref (parser);
|
||
|
+
|
||
|
+ return ata_data;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_scsi_get_info:
|
||
|
+ * @device: device to check.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Retrieve SMART information from SCSI or SAS-compliant drive.
|
||
|
+ *
|
||
|
+ * Returns: (transfer full): SCSI SMART log or %NULL in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_SCSI-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+BDSmartSCSI * bd_smart_scsi_get_info (const gchar *device, const BDExtraArg **extra, GError **error) {
|
||
|
+ const gchar *args[9] = { "smartctl", "--info", "--health", "--attributes", "--log=error", "--log=background", "--json", device, NULL };
|
||
|
+ gint status = 0;
|
||
|
+ gchar *stdout = NULL;
|
||
|
+ gchar *stderr = NULL;
|
||
|
+ JsonParser *parser;
|
||
|
+ BDSmartSCSI *data = NULL;
|
||
|
+ gboolean ret;
|
||
|
+
|
||
|
+ if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
|
||
|
+ g_prefix_error (error, "Error getting SCSI SMART info: ");
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (stdout)
|
||
|
+ g_strstrip (stdout);
|
||
|
+ if (stderr)
|
||
|
+ g_strstrip (stderr);
|
||
|
+
|
||
|
+ parser = json_parser_new ();
|
||
|
+ ret = parse_smartctl_error (status, stdout, stderr, parser, error);
|
||
|
+ g_free (stdout);
|
||
|
+ g_free (stderr);
|
||
|
+ if (! ret) {
|
||
|
+ g_prefix_error (error, "Error getting SCSI SMART info: ");
|
||
|
+ g_object_unref (parser);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ data = parse_scsi_smart (parser, error);
|
||
|
+ g_object_unref (parser);
|
||
|
+
|
||
|
+ return data;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_set_enabled:
|
||
|
+ * @device: SMART-capable device.
|
||
|
+ * @enabled: whether to enable or disable the SMART functionality
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Enables or disables SMART functionality on device.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE when the functionality was set successfully or %FALSE in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
|
||
|
+ */
|
||
|
+gboolean bd_smart_set_enabled (const gchar *device, gboolean enabled, const BDExtraArg **extra, GError **error) {
|
||
|
+ const gchar *args[5] = { "smartctl", "--json", "--smart=on", device, NULL };
|
||
|
+ gint status = 0;
|
||
|
+ gchar *stdout = NULL;
|
||
|
+ gchar *stderr = NULL;
|
||
|
+ JsonParser *parser;
|
||
|
+ gboolean ret;
|
||
|
+
|
||
|
+ if (!enabled)
|
||
|
+ args[2] = "--smart=off";
|
||
|
+
|
||
|
+ if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
|
||
|
+ g_prefix_error (error, "Error setting SMART functionality: ");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (stdout)
|
||
|
+ g_strstrip (stdout);
|
||
|
+ if (stderr)
|
||
|
+ g_strstrip (stderr);
|
||
|
+
|
||
|
+ parser = json_parser_new ();
|
||
|
+ ret = parse_smartctl_error (status, stdout, stderr, parser, error);
|
||
|
+ g_free (stdout);
|
||
|
+ g_free (stderr);
|
||
|
+ g_object_unref (parser);
|
||
|
+ if (! ret) {
|
||
|
+ g_prefix_error (error, "Error setting SMART functionality: ");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_smart_device_self_test:
|
||
|
+ * @device: device to trigger the test on.
|
||
|
+ * @operation: #BDSmartSelfTestOp self-test operation.
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
|
||
|
+ * @error: (out) (optional): place to store error (if any).
|
||
|
+ *
|
||
|
+ * Executes or aborts device self-test.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE when the self-test was trigerred successfully or %FALSE in case of an error (with @error set).
|
||
|
+ *
|
||
|
+ * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_SELFTEST
|
||
|
+ */
|
||
|
+gboolean bd_smart_device_self_test (const gchar *device, BDSmartSelfTestOp operation, const BDExtraArg **extra, GError **error) {
|
||
|
+ const gchar *args[5] = { "smartctl", "--json", "--test=", device, NULL };
|
||
|
+ gint status = 0;
|
||
|
+ gchar *stdout = NULL;
|
||
|
+ gchar *stderr = NULL;
|
||
|
+ JsonParser *parser;
|
||
|
+ gboolean ret;
|
||
|
+
|
||
|
+ switch (operation) {
|
||
|
+ case BD_SMART_SELF_TEST_OP_ABORT:
|
||
|
+ args[2] = "--abort";
|
||
|
+ break;
|
||
|
+ case BD_SMART_SELF_TEST_OP_OFFLINE:
|
||
|
+ args[2] = "--test=offline";
|
||
|
+ break;
|
||
|
+ case BD_SMART_SELF_TEST_OP_SHORT:
|
||
|
+ args[2] = "--test=short";
|
||
|
+ break;
|
||
|
+ case BD_SMART_SELF_TEST_OP_LONG:
|
||
|
+ args[2] = "--test=long";
|
||
|
+ break;
|
||
|
+ case BD_SMART_SELF_TEST_OP_CONVEYANCE:
|
||
|
+ args[2] = "--test=conveyance";
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
|
||
|
+ "Invalid self-test operation.");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
|
||
|
+ g_prefix_error (error, "Error executing SMART self-test: ");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (stdout)
|
||
|
+ g_strstrip (stdout);
|
||
|
+ if (stderr)
|
||
|
+ g_strstrip (stderr);
|
||
|
+
|
||
|
+ parser = json_parser_new ();
|
||
|
+ ret = parse_smartctl_error (status, stdout, stderr, parser, error);
|
||
|
+ g_free (stdout);
|
||
|
+ g_free (stderr);
|
||
|
+ g_object_unref (parser);
|
||
|
+ if (! ret) {
|
||
|
+ g_prefix_error (error, "Error executing SMART self-test: ");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py
|
||
|
index 25852f66..32a66202 100644
|
||
|
--- a/src/python/gi/overrides/BlockDev.py
|
||
|
+++ b/src/python/gi/overrides/BlockDev.py
|
||
|
@@ -56,6 +56,7 @@ bd_plugins = { "lvm": BlockDev.Plugin.LVM,
|
||
|
"s390": BlockDev.Plugin.S390,
|
||
|
"nvdimm": BlockDev.Plugin.NVDIMM,
|
||
|
"nvme": BlockDev.Plugin.NVME,
|
||
|
+ "smart": BlockDev.Plugin.SMART,
|
||
|
}
|
||
|
|
||
|
def _default_str(self):
|
||
|
@@ -1341,6 +1342,10 @@ class NVMEError(BlockDevError):
|
||
|
pass
|
||
|
__all__.append("NVMEError")
|
||
|
|
||
|
+class SMARTError(BlockDevError):
|
||
|
+ pass
|
||
|
+__all__.append("SMARTError")
|
||
|
+
|
||
|
class BlockDevNotImplementedError(NotImplementedError, BlockDevError):
|
||
|
pass
|
||
|
__all__.append("BlockDevNotImplementedError")
|
||
|
@@ -1393,5 +1398,8 @@ __all__.append("nvme")
|
||
|
s390 = ErrorProxy("s390", BlockDev, [(GLib.Error, S390Error)], [not_implemented_rule])
|
||
|
__all__.append("s390")
|
||
|
|
||
|
+smart = ErrorProxy("smart", BlockDev, [(GLib.Error, SMARTError)], [not_implemented_rule])
|
||
|
+__all__.append("smart")
|
||
|
+
|
||
|
utils = ErrorProxy("utils", BlockDev, [(GLib.Error, UtilsError)])
|
||
|
__all__.append("utils")
|
||
|
diff --git a/src/utils/exec.c b/src/utils/exec.c
|
||
|
index 4acaeab3..8ab1c86f 100644
|
||
|
--- a/src/utils/exec.c
|
||
|
+++ b/src/utils/exec.c
|
||
|
@@ -136,6 +136,37 @@ static void log_done (guint64 task_id, gint exit_code) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
+static const gchar ** _append_extra_args (const gchar **argv, const BDExtraArg **extra) {
|
||
|
+ const gchar **args = NULL;
|
||
|
+ guint args_len = 0;
|
||
|
+ const BDExtraArg **extra_p = NULL;
|
||
|
+ const gchar **arg_p = NULL;
|
||
|
+ guint i = 0;
|
||
|
+
|
||
|
+ if (!extra)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ args_len = g_strv_length ((gchar **) argv);
|
||
|
+ for (extra_p = extra; *extra_p; extra_p++) {
|
||
|
+ if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0))
|
||
|
+ args_len++;
|
||
|
+ if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0))
|
||
|
+ args_len++;
|
||
|
+ }
|
||
|
+
|
||
|
+ args = g_new0 (const gchar *, args_len + 1);
|
||
|
+ for (arg_p = argv; *arg_p; arg_p++)
|
||
|
+ args[i++] = *arg_p;
|
||
|
+ for (extra_p = extra; *extra_p; extra_p++) {
|
||
|
+ if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0))
|
||
|
+ args[i++] = (*extra_p)->opt;
|
||
|
+ if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0))
|
||
|
+ args[i++] = (*extra_p)->val;
|
||
|
+ }
|
||
|
+
|
||
|
+ return args;
|
||
|
+}
|
||
|
+
|
||
|
/**
|
||
|
* bd_utils_exec_and_report_error:
|
||
|
* @argv: (array zero-terminated=1): the argv array for the call
|
||
|
@@ -166,52 +197,28 @@ gboolean bd_utils_exec_and_report_error_no_progress (const gchar **argv, const B
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
- * bd_utils_exec_and_report_status_error:
|
||
|
+ * bd_utils_exec_and_capture_output_no_progress:
|
||
|
* @argv: (array zero-terminated=1): the argv array for the call
|
||
|
* @extra: (nullable) (array zero-terminated=1): extra arguments
|
||
|
- * @status: (out): place to store the status
|
||
|
+ * @output: (out) (optional): place to store stdout to
|
||
|
+ * @stderr: (out) (optional): place to store stderr to
|
||
|
+ * @status: (out): place to store the process return code
|
||
|
* @error: (out) (optional): place to store error (if any)
|
||
|
*
|
||
|
- * Returns: whether the @argv was successfully executed (no error and exit code 0) or not
|
||
|
+ * Returns: whether the @argv was successfully executed capturing the output or not
|
||
|
*/
|
||
|
-gboolean bd_utils_exec_and_report_status_error (const gchar **argv, const BDExtraArg **extra, gint *status, GError **error) {
|
||
|
+gboolean bd_utils_exec_and_capture_output_no_progress (const gchar **argv, const BDExtraArg **extra, gchar **output, gchar **stderr, gint *status, GError **error) {
|
||
|
gboolean success = FALSE;
|
||
|
gchar *stdout_data = NULL;
|
||
|
gchar *stderr_data = NULL;
|
||
|
guint64 task_id = 0;
|
||
|
const gchar **args = NULL;
|
||
|
- guint args_len = 0;
|
||
|
- const gchar **arg_p = NULL;
|
||
|
- const BDExtraArg **extra_p = NULL;
|
||
|
gint exit_status = 0;
|
||
|
- guint i = 0;
|
||
|
gchar **old_env = NULL;
|
||
|
gchar **new_env = NULL;
|
||
|
GError *l_error = NULL;
|
||
|
|
||
|
- if (extra) {
|
||
|
- args_len = g_strv_length ((gchar **) argv);
|
||
|
- for (extra_p=extra; *extra_p; extra_p++) {
|
||
|
- if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0))
|
||
|
- args_len++;
|
||
|
- if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0))
|
||
|
- args_len++;
|
||
|
- }
|
||
|
- args = g_new0 (const gchar*, args_len + 1);
|
||
|
- for (arg_p=argv; *arg_p; arg_p++, i++)
|
||
|
- args[i] = *arg_p;
|
||
|
- for (extra_p=extra; *extra_p; extra_p++) {
|
||
|
- if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0)) {
|
||
|
- args[i] = (*extra_p)->opt;
|
||
|
- i++;
|
||
|
- }
|
||
|
- if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0)) {
|
||
|
- args[i] = (*extra_p)->val;
|
||
|
- i++;
|
||
|
- }
|
||
|
- }
|
||
|
- args[i] = NULL;
|
||
|
- }
|
||
|
+ args = _append_extra_args (argv, extra);
|
||
|
|
||
|
old_env = g_get_environ ();
|
||
|
new_env = g_environ_setenv (old_env, "LC_ALL", "C.UTF-8", TRUE);
|
||
|
@@ -220,14 +227,13 @@ gboolean bd_utils_exec_and_report_status_error (const gchar **argv, const BDExtr
|
||
|
task_id = log_running (args ? args : argv);
|
||
|
success = g_spawn_sync (NULL, args ? (gchar **) args : (gchar **) argv, new_env, G_SPAWN_SEARCH_PATH,
|
||
|
NULL, NULL, &stdout_data, &stderr_data, &exit_status, error);
|
||
|
+ g_strfreev (new_env);
|
||
|
if (!success) {
|
||
|
/* error is already populated from the call */
|
||
|
- g_strfreev (new_env);
|
||
|
g_free (stdout_data);
|
||
|
g_free (stderr_data);
|
||
|
return FALSE;
|
||
|
}
|
||
|
- g_strfreev (new_env);
|
||
|
|
||
|
/* g_spawn_sync set the status in the same way waitpid() does, we need
|
||
|
to get the process exit code manually (this is similar to calling
|
||
|
@@ -255,27 +261,48 @@ gboolean bd_utils_exec_and_report_status_error (const gchar **argv, const BDExtr
|
||
|
log_done (task_id, *status);
|
||
|
|
||
|
g_free (args);
|
||
|
+ if (output)
|
||
|
+ *output = stdout_data;
|
||
|
+ else
|
||
|
+ g_free (stdout_data);
|
||
|
+ if (stderr)
|
||
|
+ *stderr = stderr_data;
|
||
|
+ else
|
||
|
+ g_free (stderr_data);
|
||
|
|
||
|
- if (*status != 0) {
|
||
|
- if (stderr_data && (g_strcmp0 ("", stderr_data) != 0)) {
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * bd_utils_exec_and_report_status_error:
|
||
|
+ * @argv: (array zero-terminated=1): the argv array for the call
|
||
|
+ * @extra: (nullable) (array zero-terminated=1): extra arguments
|
||
|
+ * @status: (out): place to store the status
|
||
|
+ * @error: (out) (optional): place to store error (if any)
|
||
|
+ *
|
||
|
+ * Returns: whether the @argv was successfully executed (no error and exit code 0) or not
|
||
|
+ */
|
||
|
+gboolean bd_utils_exec_and_report_status_error (const gchar **argv, const BDExtraArg **extra, gint *status, GError **error) {
|
||
|
+ gboolean ret;
|
||
|
+ gchar *stdout_data = NULL;
|
||
|
+ gchar *stderr_data = NULL;
|
||
|
+
|
||
|
+ ret = bd_utils_exec_and_capture_output_no_progress (argv, extra, &stdout_data, &stderr_data, status, error);
|
||
|
+
|
||
|
+ if (ret && *status != 0) {
|
||
|
+ if (stderr_data && (g_strcmp0 ("", stderr_data) != 0))
|
||
|
g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
|
||
|
"Process reported exit code %d: %s", *status, stderr_data);
|
||
|
- g_free (stdout_data);
|
||
|
- } else {
|
||
|
+ else
|
||
|
g_set_error (error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_FAILED,
|
||
|
"Process reported exit code %d: %s", *status, stdout_data);
|
||
|
- g_free (stderr_data);
|
||
|
- }
|
||
|
-
|
||
|
- return FALSE;
|
||
|
+ ret = FALSE;
|
||
|
}
|
||
|
-
|
||
|
g_free (stdout_data);
|
||
|
g_free (stderr_data);
|
||
|
- return TRUE;
|
||
|
+ return ret;
|
||
|
}
|
||
|
|
||
|
-
|
||
|
/* buffer size in bytes used to read from stdout and stderr */
|
||
|
#define _EXEC_BUF_SIZE 64*1024
|
||
|
|
||
|
@@ -359,10 +386,7 @@ _process_fd_event (gint fd, struct pollfd *poll_fd, GString *read_buffer, GStrin
|
||
|
|
||
|
static gboolean _utils_exec_and_report_progress (const gchar **argv, const BDExtraArg **extra, BDUtilsProgExtract prog_extract, const gchar *input, gint *proc_status, gchar **stdout, gchar **stderr, GError **error) {
|
||
|
const gchar **args = NULL;
|
||
|
- guint args_len = 0;
|
||
|
- const gchar **arg_p = NULL;
|
||
|
gchar *args_str = NULL;
|
||
|
- const BDExtraArg **extra_p = NULL;
|
||
|
guint64 task_id = 0;
|
||
|
guint64 progress_id = 0;
|
||
|
gchar *msg = NULL;
|
||
|
@@ -374,7 +398,6 @@ static gboolean _utils_exec_and_report_progress (const gchar **argv, const BDExt
|
||
|
gint status = 0;
|
||
|
gboolean ret = FALSE;
|
||
|
gint poll_status = 0;
|
||
|
- guint i = 0;
|
||
|
guint8 completion = 0;
|
||
|
struct pollfd fds[2] = { ZERO_INIT, ZERO_INIT };
|
||
|
int flags;
|
||
|
@@ -391,30 +414,7 @@ static gboolean _utils_exec_and_report_progress (const gchar **argv, const BDExt
|
||
|
gboolean success = TRUE;
|
||
|
GError *l_error = NULL;
|
||
|
|
||
|
- /* TODO: share this code between functions */
|
||
|
- if (extra) {
|
||
|
- args_len = g_strv_length ((gchar **) argv);
|
||
|
- for (extra_p=extra; *extra_p; extra_p++) {
|
||
|
- if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0))
|
||
|
- args_len++;
|
||
|
- if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0))
|
||
|
- args_len++;
|
||
|
- }
|
||
|
- args = g_new0 (const gchar*, args_len + 1);
|
||
|
- for (arg_p=argv; *arg_p; arg_p++, i++)
|
||
|
- args[i] = *arg_p;
|
||
|
- for (extra_p=extra; *extra_p; extra_p++) {
|
||
|
- if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "") != 0)) {
|
||
|
- args[i] = (*extra_p)->opt;
|
||
|
- i++;
|
||
|
- }
|
||
|
- if ((*extra_p)->val && (g_strcmp0 ((*extra_p)->val, "") != 0)) {
|
||
|
- args[i] = (*extra_p)->val;
|
||
|
- i++;
|
||
|
- }
|
||
|
- }
|
||
|
- args[i] = NULL;
|
||
|
- }
|
||
|
+ args = _append_extra_args (argv, extra);
|
||
|
|
||
|
task_id = log_running (args ? args : argv);
|
||
|
|
||
|
diff --git a/src/utils/exec.h b/src/utils/exec.h
|
||
|
index ab001d59..840eda6f 100644
|
||
|
--- a/src/utils/exec.h
|
||
|
+++ b/src/utils/exec.h
|
||
|
@@ -66,6 +66,7 @@ gboolean bd_utils_exec_and_report_error (const gchar **argv, const BDExtraArg **
|
||
|
gboolean bd_utils_exec_and_report_error_no_progress (const gchar **argv, const BDExtraArg **extra, GError **error);
|
||
|
gboolean bd_utils_exec_and_report_status_error (const gchar **argv, const BDExtraArg **extra, gint *status, GError **error);
|
||
|
gboolean bd_utils_exec_and_capture_output (const gchar **argv, const BDExtraArg **extra, gchar **output, GError **error);
|
||
|
+gboolean bd_utils_exec_and_capture_output_no_progress (const gchar **argv, const BDExtraArg **extra, gchar **output, gchar **stderr, gint *status, GError **error);
|
||
|
gboolean bd_utils_exec_and_report_progress (const gchar **argv, const BDExtraArg **extra, BDUtilsProgExtract prog_extract, gint *proc_status, GError **error);
|
||
|
gboolean bd_utils_exec_with_input (const gchar **argv, const gchar *input, const BDExtraArg **extra, GError **error);
|
||
|
gint bd_utils_version_cmp (const gchar *ver_string1, const gchar *ver_string2, GError **error);
|
||
|
diff --git a/tests/fake_utils/smartctl/smartctl b/tests/fake_utils/smartctl/smartctl
|
||
|
new file mode 100755
|
||
|
index 00000000..f6e644e4
|
||
|
--- /dev/null
|
||
|
+++ b/tests/fake_utils/smartctl/smartctl
|
||
|
@@ -0,0 +1,18 @@
|
||
|
+#!/bin/bash
|
||
|
+
|
||
|
+# shift the argument switches
|
||
|
+while (( "$#" )); do
|
||
|
+ if [[ $1 != --* ]]; then
|
||
|
+ break;
|
||
|
+ fi
|
||
|
+ shift
|
||
|
+done
|
||
|
+
|
||
|
+F="tests/smart_dumps/$1.json"
|
||
|
+cat "$F"
|
||
|
+
|
||
|
+# parse out the exit code
|
||
|
+EXIT_CODE=`cat "$F" | grep '"exit_status": ' | sed 's/.*: *//'`
|
||
|
+if [[ ! -z $EXIT_CODE ]]; then
|
||
|
+ exit $EXIT_CODE
|
||
|
+fi
|
||
|
diff --git a/tests/run_tests.py b/tests/run_tests.py
|
||
|
index dbceb2af..b1e5c398 100644
|
||
|
--- a/tests/run_tests.py
|
||
|
+++ b/tests/run_tests.py
|
||
|
@@ -17,7 +17,7 @@ import yaml
|
||
|
|
||
|
from utils import TestTags, get_version
|
||
|
|
||
|
-LIBDIRS = 'src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/lib/.libs:src/plugins/nvme/.libs'
|
||
|
+LIBDIRS = 'src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/lib/.libs:src/plugins/nvme/.libs:src/plugins/smart/.libs'
|
||
|
GIDIR = 'src/lib'
|
||
|
|
||
|
SKIP_CONFIG = 'skip.yml'
|
||
|
diff --git a/tests/smart_dumps/01_old_ver.json b/tests/smart_dumps/01_old_ver.json
|
||
|
new file mode 100644
|
||
|
index 00000000..d294b18e
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/01_old_ver.json
|
||
|
@@ -0,0 +1,214 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 0,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-6.0.2-zen",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--info",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "/dev/sda"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5417"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1672926876,
|
||
|
+ "asctime": "Thu Jan 5 14:54:36 2023 CET"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sda",
|
||
|
+ "info_name": "/dev/sda [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_name": "TOSHIBA THNSNH128GBST",
|
||
|
+ "serial_number": "123456789012",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 2061,
|
||
|
+ "id": 1234
|
||
|
+ },
|
||
|
+ "firmware_version": "HTRAN101",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 250069680,
|
||
|
+ "bytes": 128035676160
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 512,
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": true,
|
||
|
+ "deterministic": true,
|
||
|
+ "zeroed": true
|
||
|
+ },
|
||
|
+ "in_smartctl_database": false,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2 (minor revision not indicated)",
|
||
|
+ "major_value": 1016,
|
||
|
+ "minor_value": 0
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.1",
|
||
|
+ "value": 127
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "was completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "completion_seconds": 120
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 2,
|
||
|
+ "extended": 8
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 57,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 240,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 29029
|
||
|
+ },
|
||
|
+ "power_cycle_count": 2322,
|
||
|
+ "temperature": {
|
||
|
+ "current": 32
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/02_exit_err.json b/tests/smart_dumps/02_exit_err.json
|
||
|
new file mode 100644
|
||
|
index 00000000..cb931918
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/02_exit_err.json
|
||
|
@@ -0,0 +1,214 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-6.0.2-zen",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--info",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "/dev/sda"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5417"
|
||
|
+ },
|
||
|
+ "exit_status": 7
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1672926876,
|
||
|
+ "asctime": "Thu Jan 5 14:54:36 2023 CET"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sda",
|
||
|
+ "info_name": "/dev/sda [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_name": "TOSHIBA THNSNH128GBST",
|
||
|
+ "serial_number": "123456789012",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 2061,
|
||
|
+ "id": 1234
|
||
|
+ },
|
||
|
+ "firmware_version": "HTRAN101",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 250069680,
|
||
|
+ "bytes": 128035676160
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 512,
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": true,
|
||
|
+ "deterministic": true,
|
||
|
+ "zeroed": true
|
||
|
+ },
|
||
|
+ "in_smartctl_database": false,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2 (minor revision not indicated)",
|
||
|
+ "major_value": 1016,
|
||
|
+ "minor_value": 0
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.1",
|
||
|
+ "value": 127
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "was completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "completion_seconds": 120
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 2,
|
||
|
+ "extended": 8
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 57,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 240,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 29029
|
||
|
+ },
|
||
|
+ "power_cycle_count": 2322,
|
||
|
+ "temperature": {
|
||
|
+ "current": 32
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/03_exit_err_32.json b/tests/smart_dumps/03_exit_err_32.json
|
||
|
new file mode 100644
|
||
|
index 00000000..fd922bc3
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/03_exit_err_32.json
|
||
|
@@ -0,0 +1,214 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-6.0.2-zen",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--info",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "/dev/sda"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5417"
|
||
|
+ },
|
||
|
+ "exit_status": 32
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1672926876,
|
||
|
+ "asctime": "Thu Jan 5 14:54:36 2023 CET"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sda",
|
||
|
+ "info_name": "/dev/sda [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_name": "TOSHIBA THNSNH128GBST",
|
||
|
+ "serial_number": "123456789012",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 2061,
|
||
|
+ "id": 1234
|
||
|
+ },
|
||
|
+ "firmware_version": "HTRAN101",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 250069680,
|
||
|
+ "bytes": 128035676160
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 512,
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": true,
|
||
|
+ "deterministic": true,
|
||
|
+ "zeroed": true
|
||
|
+ },
|
||
|
+ "in_smartctl_database": false,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2 (minor revision not indicated)",
|
||
|
+ "major_value": 1016,
|
||
|
+ "minor_value": 0
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.1",
|
||
|
+ "value": 127
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "was completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "completion_seconds": 120
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 2,
|
||
|
+ "extended": 8
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 57,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 240,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 29029
|
||
|
+ },
|
||
|
+ "power_cycle_count": 2322,
|
||
|
+ "temperature": {
|
||
|
+ "current": 32
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/04_malformed.json b/tests/smart_dumps/04_malformed.json
|
||
|
new file mode 100644
|
||
|
index 00000000..9a509ed6
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/04_malformed.json
|
||
|
@@ -0,0 +1,215 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-6.0.2-zen",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--info",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "/dev/sda"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5417"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ hahaha
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1672926876,
|
||
|
+ "asctime": "Thu Jan 5 14:54:36 2023 CET"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sda",
|
||
|
+ "info_name": "/dev/sda [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_name": "TOSHIBA THNSNH128GBST",
|
||
|
+ "serial_number": "123456789012",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 2061,
|
||
|
+ "id": 1234
|
||
|
+ },
|
||
|
+ "firmware_version": "HTRAN101",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 250069680,
|
||
|
+ "bytes": 128035676160
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 512,
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": true,
|
||
|
+ "deterministic": true,
|
||
|
+ "zeroed": true
|
||
|
+ },
|
||
|
+ "in_smartctl_database": false,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2 (minor revision not indicated)",
|
||
|
+ "major_value": 1016,
|
||
|
+ "minor_value": 0
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.1",
|
||
|
+ "value": 127
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "was completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "completion_seconds": 120
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 2,
|
||
|
+ "extended": 8
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 57,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 240,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 29029
|
||
|
+ },
|
||
|
+ "power_cycle_count": 2322,
|
||
|
+ "temperature": {
|
||
|
+ "current": 32
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/05_empty.json b/tests/smart_dumps/05_empty.json
|
||
|
new file mode 100644
|
||
|
index 00000000..e69de29b
|
||
|
diff --git a/tests/smart_dumps/Biwintech_SSD_SX500.bin b/tests/smart_dumps/Biwintech_SSD_SX500.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..0afd433c
|
||
|
Binary files /dev/null and b/tests/smart_dumps/Biwintech_SSD_SX500.bin differ
|
||
|
diff --git a/tests/smart_dumps/GIGABYTE_GP-GSTFS31100TNTD.bin b/tests/smart_dumps/GIGABYTE_GP-GSTFS31100TNTD.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..72022d02
|
||
|
Binary files /dev/null and b/tests/smart_dumps/GIGABYTE_GP-GSTFS31100TNTD.bin differ
|
||
|
diff --git a/tests/smart_dumps/HGST_HMS5C4040BLE640.json b/tests/smart_dumps/HGST_HMS5C4040BLE640.json
|
||
|
new file mode 100644
|
||
|
index 00000000..d1091851
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/HGST_HMS5C4040BLE640.json
|
||
|
@@ -0,0 +1,523 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.19.1-gentoo",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--info",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "--device=ata",
|
||
|
+ "--badsum=ignore",
|
||
|
+ "/dev/sda"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5387"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1673282309,
|
||
|
+ "asctime": "Mon Jan 9 17:38:29 2023 CET"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sda",
|
||
|
+ "info_name": "/dev/sda",
|
||
|
+ "type": "ata",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_family": "HGST MegaScale 4000",
|
||
|
+ "model_name": "HGST HMS5C4040BLE640",
|
||
|
+ "serial_number": "PL01234567890J",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 3274,
|
||
|
+ "id": 1234
|
||
|
+ },
|
||
|
+ "firmware_version": "MPAOA5D0",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 7814037168,
|
||
|
+ "bytes": 4000787030016
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 4096,
|
||
|
+ "rotation_rate": 5700,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 2,
|
||
|
+ "name": "3.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": false
|
||
|
+ },
|
||
|
+ "in_smartctl_database": true,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ATA8-ACS T13/1699-D revision 4",
|
||
|
+ "major_value": 508,
|
||
|
+ "minor_value": 41
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.0",
|
||
|
+ "value": 63
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 130,
|
||
|
+ "string": "was completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "completion_seconds": 28
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 1,
|
||
|
+ "extended": 730
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 61,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 16,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 11,
|
||
|
+ "string": "PO-R-- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 135,
|
||
|
+ "worst": 135,
|
||
|
+ "thresh": 54,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 98,
|
||
|
+ "string": "98"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 3,
|
||
|
+ "name": "Spin_Up_Time",
|
||
|
+ "value": 127,
|
||
|
+ "worst": 127,
|
||
|
+ "thresh": 24,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 7,
|
||
|
+ "string": "POS--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 47280488996,
|
||
|
+ "string": "548 (Average 547)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 4,
|
||
|
+ "name": "Start_Stop_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 2607,
|
||
|
+ "string": "2607"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 5,
|
||
|
+ "name": "Reallocated_Sector_Ct",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 5,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 7,
|
||
|
+ "name": "Seek_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 67,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 11,
|
||
|
+ "string": "PO-R-- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 8,
|
||
|
+ "name": "Seek_Time_Performance",
|
||
|
+ "value": 111,
|
||
|
+ "worst": 111,
|
||
|
+ "thresh": 20,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 43,
|
||
|
+ "string": "43"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 9,
|
||
|
+ "name": "Power_On_Hours",
|
||
|
+ "value": 92,
|
||
|
+ "worst": 92,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 60390,
|
||
|
+ "string": "60390"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 10,
|
||
|
+ "name": "Spin_Retry_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 60,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 12,
|
||
|
+ "name": "Power_Cycle_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 66,
|
||
|
+ "string": "66"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 192,
|
||
|
+ "name": "Power-Off_Retract_Count",
|
||
|
+ "value": 98,
|
||
|
+ "worst": 98,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 2640,
|
||
|
+ "string": "2640"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 193,
|
||
|
+ "name": "Load_Cycle_Count",
|
||
|
+ "value": 98,
|
||
|
+ "worst": 98,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 2640,
|
||
|
+ "string": "2640"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 194,
|
||
|
+ "name": "Temperature_Celsius",
|
||
|
+ "value": 171,
|
||
|
+ "worst": 171,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "-O---- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 188979937315,
|
||
|
+ "string": "35 (Min/Max 21/44)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 196,
|
||
|
+ "name": "Reallocated_Event_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 197,
|
||
|
+ "name": "Current_Pending_Sector",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 34,
|
||
|
+ "string": "-O---K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 198,
|
||
|
+ "name": "Offline_Uncorrectable",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 8,
|
||
|
+ "string": "---R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 199,
|
||
|
+ "name": "UDMA_CRC_Error_Count",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 60390
|
||
|
+ },
|
||
|
+ "power_cycle_count": 66,
|
||
|
+ "temperature": {
|
||
|
+ "current": 35
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/HGST_HUS726060ALA640.json b/tests/smart_dumps/HGST_HUS726060ALA640.json
|
||
|
new file mode 100644
|
||
|
index 00000000..3308a8d6
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/HGST_HUS726060ALA640.json
|
||
|
@@ -0,0 +1,545 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.19.1-gentoo",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--info",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "--device=ata",
|
||
|
+ "--badsum=ignore",
|
||
|
+ "/dev/sdd"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5387"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1673282317,
|
||
|
+ "asctime": "Mon Jan 9 17:38:37 2023 CET"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdd",
|
||
|
+ "info_name": "/dev/sdd",
|
||
|
+ "type": "ata",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_family": "HGST Ultrastar He6",
|
||
|
+ "model_name": "HGST HUS726060ALA640",
|
||
|
+ "serial_number": "AR01234567890C",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 3274,
|
||
|
+ "id": 1234
|
||
|
+ },
|
||
|
+ "firmware_version": "AHGNT1EN",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 11721045168,
|
||
|
+ "bytes": 6001175126016
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 512,
|
||
|
+ "rotation_rate": 7200,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 2,
|
||
|
+ "name": "3.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": false
|
||
|
+ },
|
||
|
+ "in_smartctl_database": true,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2, ATA8-ACS T13/1699-D revision 4",
|
||
|
+ "major_value": 1020,
|
||
|
+ "minor_value": 41
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.1",
|
||
|
+ "value": 127
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 130,
|
||
|
+ "string": "was completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "completion_seconds": 57
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 2,
|
||
|
+ "extended": 916
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 61,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 86,
|
||
|
+ "worst": 86,
|
||
|
+ "thresh": 16,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 11,
|
||
|
+ "string": "PO-R-- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 4980812,
|
||
|
+ "string": "4980812"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 132,
|
||
|
+ "worst": 132,
|
||
|
+ "thresh": 54,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 84,
|
||
|
+ "string": "84"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 3,
|
||
|
+ "name": "Spin_Up_Time",
|
||
|
+ "value": 192,
|
||
|
+ "worst": 192,
|
||
|
+ "thresh": 24,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 7,
|
||
|
+ "string": "POS--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 34013703,
|
||
|
+ "string": "519 (Average 519)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 4,
|
||
|
+ "name": "Start_Stop_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 1654,
|
||
|
+ "string": "1654"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 5,
|
||
|
+ "name": "Reallocated_Sector_Ct",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 5,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 1040,
|
||
|
+ "string": "1040"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 7,
|
||
|
+ "name": "Seek_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 67,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 11,
|
||
|
+ "string": "PO-R-- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 8,
|
||
|
+ "name": "Seek_Time_Performance",
|
||
|
+ "value": 130,
|
||
|
+ "worst": 130,
|
||
|
+ "thresh": 20,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 12,
|
||
|
+ "string": "12"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 9,
|
||
|
+ "name": "Power_On_Hours",
|
||
|
+ "value": 94,
|
||
|
+ "worst": 94,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 43177,
|
||
|
+ "string": "43177"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 10,
|
||
|
+ "name": "Spin_Retry_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 60,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 12,
|
||
|
+ "name": "Power_Cycle_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 55,
|
||
|
+ "string": "55"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 22,
|
||
|
+ "name": "Helium_Level",
|
||
|
+ "value": 82,
|
||
|
+ "worst": 82,
|
||
|
+ "thresh": 25,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 35,
|
||
|
+ "string": "PO---K ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 82,
|
||
|
+ "string": "82"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 192,
|
||
|
+ "name": "Power-Off_Retract_Count",
|
||
|
+ "value": 99,
|
||
|
+ "worst": 99,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 1736,
|
||
|
+ "string": "1736"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 193,
|
||
|
+ "name": "Load_Cycle_Count",
|
||
|
+ "value": 99,
|
||
|
+ "worst": 99,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 1736,
|
||
|
+ "string": "1736"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 194,
|
||
|
+ "name": "Temperature_Celsius",
|
||
|
+ "value": 206,
|
||
|
+ "worst": 206,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "-O---- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 231929413661,
|
||
|
+ "string": "29 (Min/Max 18/54)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 196,
|
||
|
+ "name": "Reallocated_Event_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 197,
|
||
|
+ "name": "Current_Pending_Sector",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 34,
|
||
|
+ "string": "-O---K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 878,
|
||
|
+ "string": "878"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 198,
|
||
|
+ "name": "Offline_Uncorrectable",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 8,
|
||
|
+ "string": "---R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 1272,
|
||
|
+ "string": "1272"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 199,
|
||
|
+ "name": "UDMA_CRC_Error_Count",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 43177
|
||
|
+ },
|
||
|
+ "power_cycle_count": 55,
|
||
|
+ "temperature": {
|
||
|
+ "current": 29
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/HGST_HUSMR3280ASS200.json b/tests/smart_dumps/HGST_HUSMR3280ASS200.json
|
||
|
new file mode 100644
|
||
|
index 00000000..b40196ad
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/HGST_HUSMR3280ASS200.json
|
||
|
@@ -0,0 +1,155 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.18.18-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "-a",
|
||
|
+ "--json",
|
||
|
+ "/dev/sdc"
|
||
|
+ ],
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1661183098,
|
||
|
+ "asctime": "Mon Aug 22 11:44:58 2022 EDT"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdc",
|
||
|
+ "info_name": "/dev/sdc",
|
||
|
+ "type": "scsi",
|
||
|
+ "protocol": "SCSI"
|
||
|
+ },
|
||
|
+ "scsi_vendor": "HGST",
|
||
|
+ "scsi_product": "HUSMR3280ASS200",
|
||
|
+ "scsi_model_name": "HGST HUSMR3280ASS200",
|
||
|
+ "scsi_revision": "J172",
|
||
|
+ "scsi_version": "SPC-4",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 1562824368,
|
||
|
+ "bytes": 800166076416
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "scsi_lb_provisioning": {
|
||
|
+ "name": "resource provisioned",
|
||
|
+ "value": 1,
|
||
|
+ "management_enabled": {
|
||
|
+ "name": "LBPME",
|
||
|
+ "value": 1
|
||
|
+ },
|
||
|
+ "read_zeros": {
|
||
|
+ "name": "LBPRZ",
|
||
|
+ "value": 1
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "scsi_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "logical_unit_id": "0x5000ccaccaccacca",
|
||
|
+ "serial_number": "74747474X",
|
||
|
+ "device_type": {
|
||
|
+ "scsi_terminology": "Peripheral Device Type [PDT]",
|
||
|
+ "scsi_value": 0,
|
||
|
+ "name": "disk"
|
||
|
+ },
|
||
|
+ "scsi_transport_protocol": {
|
||
|
+ "name": "SAS (SPL-4)",
|
||
|
+ "value": 6
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "temperature_warning": {
|
||
|
+ "enabled": false
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "scsi_percentage_used_endurance_indicator": 0,
|
||
|
+ "temperature": {
|
||
|
+ "current": 27,
|
||
|
+ "drive_trip": 60
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 4596,
|
||
|
+ "minutes": 24
|
||
|
+ },
|
||
|
+ "scsi_start_stop_cycle_counter": {
|
||
|
+ "year_of_manufacture": "2019",
|
||
|
+ "week_of_manufacture": "43",
|
||
|
+ "specified_cycle_count_over_device_lifetime": 0,
|
||
|
+ "accumulated_start_stop_cycles": 0,
|
||
|
+ "specified_load_unload_count_over_device_lifetime": 0,
|
||
|
+ "accumulated_load_unload_cycles": 0
|
||
|
+ },
|
||
|
+ "scsi_error_counter_log": {
|
||
|
+ "read": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "48.639",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "write": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "2473.166",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "verify": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "7.988",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_0": {
|
||
|
+ "code": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "Background long"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 1,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_1": {
|
||
|
+ "code": {
|
||
|
+ "value": 1,
|
||
|
+ "string": "Background short"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 1,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_extended_self_test_seconds": 1320
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/Hitachi_HDS5C3020ALA632.json b/tests/smart_dumps/Hitachi_HDS5C3020ALA632.json
|
||
|
new file mode 100644
|
||
|
index 00000000..316ce882
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/Hitachi_HDS5C3020ALA632.json
|
||
|
@@ -0,0 +1,519 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-6.2.7-gentoo",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--info",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--json",
|
||
|
+ "/dev/sdg"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5440"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1687967601,
|
||
|
+ "asctime": "Wed Jun 28 17:53:21 2023 CEST"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdg",
|
||
|
+ "info_name": "/dev/sdg [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_family": "Hitachi Deskstar 5K3000",
|
||
|
+ "model_name": "Hitachi HDS5C3020ALA632",
|
||
|
+ "serial_number": "ML0123456789MK",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 3274,
|
||
|
+ "id": 123456789
|
||
|
+ },
|
||
|
+ "firmware_version": "ML6OA5C0",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 3907029168,
|
||
|
+ "bytes": 2000398934016
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 512,
|
||
|
+ "rotation_rate": 5940,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 2,
|
||
|
+ "name": "3.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": false
|
||
|
+ },
|
||
|
+ "in_smartctl_database": true,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ATA8-ACS T13/1699-D revision 4",
|
||
|
+ "major_value": 508,
|
||
|
+ "minor_value": 41
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 2.6",
|
||
|
+ "value": 31
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 132,
|
||
|
+ "string": "was suspended by an interrupting command from host"
|
||
|
+ },
|
||
|
+ "completion_seconds": 24155
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 1,
|
||
|
+ "extended": 403
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 61,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 16,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 11,
|
||
|
+ "string": "PO-R-- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 131,
|
||
|
+ "worst": 131,
|
||
|
+ "thresh": 54,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 112,
|
||
|
+ "string": "112"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 3,
|
||
|
+ "name": "Spin_Up_Time",
|
||
|
+ "value": 137,
|
||
|
+ "worst": 137,
|
||
|
+ "thresh": 24,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 7,
|
||
|
+ "string": "POS--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 34385822096,
|
||
|
+ "string": "400 (Average 398)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 4,
|
||
|
+ "name": "Start_Stop_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 435,
|
||
|
+ "string": "435"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 5,
|
||
|
+ "name": "Reallocated_Sector_Ct",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 5,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 7,
|
||
|
+ "name": "Seek_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 67,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 11,
|
||
|
+ "string": "PO-R-- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 8,
|
||
|
+ "name": "Seek_Time_Performance",
|
||
|
+ "value": 146,
|
||
|
+ "worst": 146,
|
||
|
+ "thresh": 20,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 29,
|
||
|
+ "string": "29"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 9,
|
||
|
+ "name": "Power_On_Hours",
|
||
|
+ "value": 86,
|
||
|
+ "worst": 86,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 102088,
|
||
|
+ "string": "102088"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 10,
|
||
|
+ "name": "Spin_Retry_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 60,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 12,
|
||
|
+ "name": "Power_Cycle_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 157,
|
||
|
+ "string": "157"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 192,
|
||
|
+ "name": "Power-Off_Retract_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 505,
|
||
|
+ "string": "505"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 193,
|
||
|
+ "name": "Load_Cycle_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 505,
|
||
|
+ "string": "505"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 194,
|
||
|
+ "name": "Temperature_Celsius",
|
||
|
+ "value": 157,
|
||
|
+ "worst": 157,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "-O---- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 223339479078,
|
||
|
+ "string": "38 (Min/Max 18/52)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 196,
|
||
|
+ "name": "Reallocated_Event_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 197,
|
||
|
+ "name": "Current_Pending_Sector",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 34,
|
||
|
+ "string": "-O---K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 198,
|
||
|
+ "name": "Offline_Uncorrectable",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 8,
|
||
|
+ "string": "---R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 199,
|
||
|
+ "name": "UDMA_CRC_Error_Count",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 4,
|
||
|
+ "string": "4"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 102088
|
||
|
+ },
|
||
|
+ "power_cycle_count": 157,
|
||
|
+ "temperature": {
|
||
|
+ "current": 38
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/Hitachi_HDS721010CLA632.bin b/tests/smart_dumps/Hitachi_HDS721010CLA632.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..b721ef7b
|
||
|
Binary files /dev/null and b/tests/smart_dumps/Hitachi_HDS721010CLA632.bin differ
|
||
|
diff --git a/tests/smart_dumps/IBM_IC25N020ATCS04-0.bin b/tests/smart_dumps/IBM_IC25N020ATCS04-0.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..635fcfe2
|
||
|
Binary files /dev/null and b/tests/smart_dumps/IBM_IC25N020ATCS04-0.bin differ
|
||
|
diff --git a/tests/smart_dumps/INTEL_SSDSC2BB120G4L.json b/tests/smart_dumps/INTEL_SSDSC2BB120G4L.json
|
||
|
new file mode 100644
|
||
|
index 00000000..2967242e
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/INTEL_SSDSC2BB120G4L.json
|
||
|
@@ -0,0 +1,699 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.19.11-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--info",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "--badsum=ignore",
|
||
|
+ "/dev/sdr"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5319"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1674654465,
|
||
|
+ "asctime": "Wed Jan 25 08:47:45 2023 EST"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdr",
|
||
|
+ "info_name": "/dev/sdr [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_name": "INTEL SSDSC2BB120G4L",
|
||
|
+ "serial_number": "PHWL12345678901LGN",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 6083300,
|
||
|
+ "id": 1234567890
|
||
|
+ },
|
||
|
+ "firmware_version": "D201LD12",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 234441648,
|
||
|
+ "bytes": 120034123776
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 4096,
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": true,
|
||
|
+ "deterministic": true,
|
||
|
+ "zeroed": true
|
||
|
+ },
|
||
|
+ "in_smartctl_database": false,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2 T13/2015-D revision 3",
|
||
|
+ "major_value": 1020,
|
||
|
+ "minor_value": 272
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 2.6",
|
||
|
+ "value": 31
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 2,
|
||
|
+ "string": "3.0 Gb/s",
|
||
|
+ "units_per_second": 30,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "was never started"
|
||
|
+ },
|
||
|
+ "completion_seconds": 0
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 1,
|
||
|
+ "extended": 2,
|
||
|
+ "conveyance": 2
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 121,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": true,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 61,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 1,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 5,
|
||
|
+ "name": "Reallocated_Sector_Ct",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 9,
|
||
|
+ "name": "Power_On_Hours",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 63357,
|
||
|
+ "string": "63357"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 12,
|
||
|
+ "name": "Power_Cycle_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 14,
|
||
|
+ "string": "14"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 170,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 10,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 171,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 172,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 174,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 12,
|
||
|
+ "string": "12"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 175,
|
||
|
+ "name": "Program_Fail_Count_Chip",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 10,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 1606413648477,
|
||
|
+ "string": "1606413648477"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 183,
|
||
|
+ "name": "Runtime_Bad_Block",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 184,
|
||
|
+ "name": "End-to-End_Error",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 90,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 187,
|
||
|
+ "name": "Reported_Uncorrect",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 190,
|
||
|
+ "name": "Airflow_Temperature_Cel",
|
||
|
+ "value": 80,
|
||
|
+ "worst": 78,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 34,
|
||
|
+ "string": "-O---K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 369688596,
|
||
|
+ "string": "20 (Min/Max 9/22)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 192,
|
||
|
+ "name": "Power-Off_Retract_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 12,
|
||
|
+ "string": "12"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 194,
|
||
|
+ "name": "Temperature_Celsius",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 34,
|
||
|
+ "string": "-O---K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 27,
|
||
|
+ "string": "27"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 197,
|
||
|
+ "name": "Current_Pending_Sector",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 199,
|
||
|
+ "name": "UDMA_CRC_Error_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 62,
|
||
|
+ "string": "-OSRCK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 225,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 226,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 61,
|
||
|
+ "string": "61"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 227,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 100,
|
||
|
+ "string": "100"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 228,
|
||
|
+ "name": "Power-off_Retract_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 3801437,
|
||
|
+ "string": "3801437"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 232,
|
||
|
+ "name": "Available_Reservd_Space",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 10,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 233,
|
||
|
+ "name": "Media_Wearout_Indicator",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 234,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 241,
|
||
|
+ "name": "Total_LBAs_Written",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 242,
|
||
|
+ "name": "Total_LBAs_Read",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 13505,
|
||
|
+ "string": "13505"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 63357
|
||
|
+ },
|
||
|
+ "power_cycle_count": 14,
|
||
|
+ "temperature": {
|
||
|
+ "current": 27
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/KINGSTON_SA400S37480G_SBFKQ13.bin b/tests/smart_dumps/KINGSTON_SA400S37480G_SBFKQ13.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..781aa9c4
|
||
|
Binary files /dev/null and b/tests/smart_dumps/KINGSTON_SA400S37480G_SBFKQ13.bin differ
|
||
|
diff --git a/tests/smart_dumps/Maxtor_6Y120P0.bin b/tests/smart_dumps/Maxtor_6Y120P0.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..4ea3ec5a
|
||
|
Binary files /dev/null and b/tests/smart_dumps/Maxtor_6Y120P0.bin differ
|
||
|
diff --git a/tests/smart_dumps/Patriot_Burst_240GB.bin b/tests/smart_dumps/Patriot_Burst_240GB.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..cac25077
|
||
|
Binary files /dev/null and b/tests/smart_dumps/Patriot_Burst_240GB.bin differ
|
||
|
diff --git a/tests/smart_dumps/SAMSUNG_HS122JC.bin b/tests/smart_dumps/SAMSUNG_HS122JC.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..3a10e642
|
||
|
Binary files /dev/null and b/tests/smart_dumps/SAMSUNG_HS122JC.bin differ
|
||
|
diff --git a/tests/smart_dumps/SAMSUNG_MMCRE28G5MXP-0VBH1.bin b/tests/smart_dumps/SAMSUNG_MMCRE28G5MXP-0VBH1.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..ca8bb7ef
|
||
|
Binary files /dev/null and b/tests/smart_dumps/SAMSUNG_MMCRE28G5MXP-0VBH1.bin differ
|
||
|
diff --git a/tests/smart_dumps/SEAGATE_ST600MP0036.json b/tests/smart_dumps/SEAGATE_ST600MP0036.json
|
||
|
new file mode 100644
|
||
|
index 00000000..4ff7cae3
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/SEAGATE_ST600MP0036.json
|
||
|
@@ -0,0 +1,168 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.18.18-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "-a",
|
||
|
+ "--json",
|
||
|
+ "/dev/sdb"
|
||
|
+ ],
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1661183078,
|
||
|
+ "asctime": "Mon Aug 22 11:44:38 2022 EDT"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdb",
|
||
|
+ "info_name": "/dev/sdb",
|
||
|
+ "type": "scsi",
|
||
|
+ "protocol": "SCSI"
|
||
|
+ },
|
||
|
+ "scsi_vendor": "SEAGATE",
|
||
|
+ "scsi_product": "ST600MP0036",
|
||
|
+ "scsi_model_name": "SEAGATE ST600MP0036",
|
||
|
+ "scsi_revision": "KT3A",
|
||
|
+ "scsi_version": "SPC-4",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 1172123568,
|
||
|
+ "bytes": 600127266816
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "scsi_protection_type": 2,
|
||
|
+ "scsi_protection_interval_bytes_per_lb": 8,
|
||
|
+ "scsi_lb_provisioning": {
|
||
|
+ "name": "fully provisioned",
|
||
|
+ "value": 0,
|
||
|
+ "management_enabled": {
|
||
|
+ "name": "LBPME",
|
||
|
+ "value": 0
|
||
|
+ },
|
||
|
+ "read_zeros": {
|
||
|
+ "name": "LBPRZ",
|
||
|
+ "value": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "rotation_rate": 15000,
|
||
|
+ "form_factor": {
|
||
|
+ "scsi_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "logical_unit_id": "0x5000c50accaccacc",
|
||
|
+ "serial_number": "WAF1ABCD",
|
||
|
+ "device_type": {
|
||
|
+ "scsi_terminology": "Peripheral Device Type [PDT]",
|
||
|
+ "scsi_value": 0,
|
||
|
+ "name": "disk"
|
||
|
+ },
|
||
|
+ "scsi_transport_protocol": {
|
||
|
+ "name": "SAS (SPL-4)",
|
||
|
+ "value": 6
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "temperature_warning": {
|
||
|
+ "enabled": false
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "scsi_format_status": {
|
||
|
+ "total_new_block_since_format": 0
|
||
|
+ },
|
||
|
+ "temperature": {
|
||
|
+ "current": 29,
|
||
|
+ "drive_trip": 60
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 4665,
|
||
|
+ "minutes": 46
|
||
|
+ },
|
||
|
+ "scsi_start_stop_cycle_counter": {
|
||
|
+ "year_of_manufacture": "2019",
|
||
|
+ "week_of_manufacture": "50",
|
||
|
+ "specified_cycle_count_over_device_lifetime": 10000,
|
||
|
+ "accumulated_start_stop_cycles": 1090,
|
||
|
+ "specified_load_unload_count_over_device_lifetime": 300000,
|
||
|
+ "accumulated_load_unload_cycles": 1181
|
||
|
+ },
|
||
|
+ "scsi_grown_defect_list": 0,
|
||
|
+ "scsi_error_counter_log": {
|
||
|
+ "read": {
|
||
|
+ "errors_corrected_by_eccfast": 170693715,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 170693715,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "88.818",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "write": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "2256.473",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "verify": {
|
||
|
+ "errors_corrected_by_eccfast": 26102529,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 26102529,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "13.575",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_0": {
|
||
|
+ "code": {
|
||
|
+ "value": 7,
|
||
|
+ "string": "Reserved(7)"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "failed_segment": {
|
||
|
+ "value": 64,
|
||
|
+ "aka": "self_test_number"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 3,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_1": {
|
||
|
+ "code": {
|
||
|
+ "value": 1,
|
||
|
+ "string": "Background short"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "failed_segment": {
|
||
|
+ "value": 96,
|
||
|
+ "aka": "self_test_number"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 1,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_extended_self_test_seconds": 3360
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/SiliconPower_SSD_SBFM61.3.bin b/tests/smart_dumps/SiliconPower_SSD_SBFM61.3.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..f10aefc4
|
||
|
Binary files /dev/null and b/tests/smart_dumps/SiliconPower_SSD_SBFM61.3.bin differ
|
||
|
diff --git a/tests/smart_dumps/TOSHIBA_AL15SEB120NY.json b/tests/smart_dumps/TOSHIBA_AL15SEB120NY.json
|
||
|
new file mode 100644
|
||
|
index 00000000..8c0ed417
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/TOSHIBA_AL15SEB120NY.json
|
||
|
@@ -0,0 +1,149 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.18.18-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "-a",
|
||
|
+ "--json",
|
||
|
+ "/dev/sdd"
|
||
|
+ ],
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1661183115,
|
||
|
+ "asctime": "Mon Aug 22 11:45:15 2022 EDT"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdd",
|
||
|
+ "info_name": "/dev/sdd",
|
||
|
+ "type": "scsi",
|
||
|
+ "protocol": "SCSI"
|
||
|
+ },
|
||
|
+ "scsi_vendor": "TOSHIBA",
|
||
|
+ "scsi_product": "AL15SEB120NY",
|
||
|
+ "scsi_model_name": "TOSHIBA AL15SEB120NY",
|
||
|
+ "scsi_revision": "EF06",
|
||
|
+ "scsi_version": "SPC-4",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 2344225968,
|
||
|
+ "bytes": 1200243695616
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "scsi_protection_type": 2,
|
||
|
+ "scsi_protection_interval_bytes_per_lb": 8,
|
||
|
+ "rotation_rate": 10000,
|
||
|
+ "form_factor": {
|
||
|
+ "scsi_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "logical_unit_id": "0x5000039ccaccacca",
|
||
|
+ "serial_number": "Z9W012345678",
|
||
|
+ "device_type": {
|
||
|
+ "scsi_terminology": "Peripheral Device Type [PDT]",
|
||
|
+ "scsi_value": 0,
|
||
|
+ "name": "disk"
|
||
|
+ },
|
||
|
+ "scsi_transport_protocol": {
|
||
|
+ "name": "SAS (SPL-4)",
|
||
|
+ "value": 6
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "temperature_warning": {
|
||
|
+ "enabled": false
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "temperature": {
|
||
|
+ "current": 24,
|
||
|
+ "drive_trip": 65
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 4617,
|
||
|
+ "minutes": 5
|
||
|
+ },
|
||
|
+ "scsi_start_stop_cycle_counter": {
|
||
|
+ "year_of_manufacture": "2019",
|
||
|
+ "week_of_manufacture": "51",
|
||
|
+ "specified_cycle_count_over_device_lifetime": 50000,
|
||
|
+ "accumulated_start_stop_cycles": 1073,
|
||
|
+ "specified_load_unload_count_over_device_lifetime": 600000,
|
||
|
+ "accumulated_load_unload_cycles": 5500
|
||
|
+ },
|
||
|
+ "scsi_grown_defect_list": 0,
|
||
|
+ "scsi_error_counter_log": {
|
||
|
+ "read": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "416922.085",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "write": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "2856.904",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "verify": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "20.321",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_0": {
|
||
|
+ "code": {
|
||
|
+ "value": 7,
|
||
|
+ "string": "Reserved(7)"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "failed_segment": {
|
||
|
+ "value": 80,
|
||
|
+ "aka": "self_test_number"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 3,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_1": {
|
||
|
+ "code": {
|
||
|
+ "value": 1,
|
||
|
+ "string": "Background short"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 1,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_extended_self_test_seconds": 6429
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/TOSHIBA_AL15SEB18EQY.json b/tests/smart_dumps/TOSHIBA_AL15SEB18EQY.json
|
||
|
new file mode 100644
|
||
|
index 00000000..d4cf1f4b
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/TOSHIBA_AL15SEB18EQY.json
|
||
|
@@ -0,0 +1,150 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.18.18-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "-a",
|
||
|
+ "--json",
|
||
|
+ "/dev/sde"
|
||
|
+ ],
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1661183151,
|
||
|
+ "asctime": "Mon Aug 22 11:45:51 2022 EDT"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sde",
|
||
|
+ "info_name": "/dev/sde",
|
||
|
+ "type": "scsi",
|
||
|
+ "protocol": "SCSI"
|
||
|
+ },
|
||
|
+ "scsi_vendor": "TOSHIBA",
|
||
|
+ "scsi_product": "AL15SEB18EQY",
|
||
|
+ "scsi_model_name": "TOSHIBA AL15SEB18EQY",
|
||
|
+ "scsi_revision": "EF06",
|
||
|
+ "scsi_version": "SPC-4",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 3516328368,
|
||
|
+ "bytes": 1800360124416
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 4096,
|
||
|
+ "scsi_protection_type": 2,
|
||
|
+ "scsi_protection_interval_bytes_per_lb": 8,
|
||
|
+ "rotation_rate": 10000,
|
||
|
+ "form_factor": {
|
||
|
+ "scsi_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "logical_unit_id": "0x5000039ccaccacca",
|
||
|
+ "serial_number": "Y9S012345678",
|
||
|
+ "device_type": {
|
||
|
+ "scsi_terminology": "Peripheral Device Type [PDT]",
|
||
|
+ "scsi_value": 0,
|
||
|
+ "name": "disk"
|
||
|
+ },
|
||
|
+ "scsi_transport_protocol": {
|
||
|
+ "name": "SAS (SPL-4)",
|
||
|
+ "value": 6
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "temperature_warning": {
|
||
|
+ "enabled": false
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "temperature": {
|
||
|
+ "current": 25,
|
||
|
+ "drive_trip": 65
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 4616,
|
||
|
+ "minutes": 36
|
||
|
+ },
|
||
|
+ "scsi_start_stop_cycle_counter": {
|
||
|
+ "year_of_manufacture": "2019",
|
||
|
+ "week_of_manufacture": "48",
|
||
|
+ "specified_cycle_count_over_device_lifetime": 50000,
|
||
|
+ "accumulated_start_stop_cycles": 1073,
|
||
|
+ "specified_load_unload_count_over_device_lifetime": 600000,
|
||
|
+ "accumulated_load_unload_cycles": 4996
|
||
|
+ },
|
||
|
+ "scsi_grown_defect_list": 0,
|
||
|
+ "scsi_error_counter_log": {
|
||
|
+ "read": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "548659.318",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "write": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "3346.357",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "verify": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "24.840",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_0": {
|
||
|
+ "code": {
|
||
|
+ "value": 7,
|
||
|
+ "string": "Reserved(7)"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "failed_segment": {
|
||
|
+ "value": 80,
|
||
|
+ "aka": "self_test_number"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 3,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_1": {
|
||
|
+ "code": {
|
||
|
+ "value": 1,
|
||
|
+ "string": "Background short"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 1,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_extended_self_test_seconds": 8693
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/TOSHIBA_KPM5XMUG400G.json b/tests/smart_dumps/TOSHIBA_KPM5XMUG400G.json
|
||
|
new file mode 100644
|
||
|
index 00000000..35807b3f
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/TOSHIBA_KPM5XMUG400G.json
|
||
|
@@ -0,0 +1,153 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.18.18-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "-a",
|
||
|
+ "--json",
|
||
|
+ "/dev/sda"
|
||
|
+ ],
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1661183054,
|
||
|
+ "asctime": "Mon Aug 22 11:44:14 2022 EDT"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sda",
|
||
|
+ "info_name": "/dev/sda",
|
||
|
+ "type": "scsi",
|
||
|
+ "protocol": "SCSI"
|
||
|
+ },
|
||
|
+ "scsi_vendor": "TOSHIBA",
|
||
|
+ "scsi_product": "KPM5XMUG400G",
|
||
|
+ "scsi_model_name": "TOSHIBA KPM5XMUG400G",
|
||
|
+ "scsi_revision": "B026",
|
||
|
+ "scsi_version": "SPC-4",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 781422768,
|
||
|
+ "bytes": 400088457216
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 4096,
|
||
|
+ "scsi_lb_provisioning": {
|
||
|
+ "name": "resource provisioned",
|
||
|
+ "value": 1,
|
||
|
+ "management_enabled": {
|
||
|
+ "name": "LBPME",
|
||
|
+ "value": 1
|
||
|
+ },
|
||
|
+ "read_zeros": {
|
||
|
+ "name": "LBPRZ",
|
||
|
+ "value": 1
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "scsi_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "logical_unit_id": "0x58ce38eeeeeeeeee",
|
||
|
+ "serial_number": "99A012345678",
|
||
|
+ "device_type": {
|
||
|
+ "scsi_terminology": "Peripheral Device Type [PDT]",
|
||
|
+ "scsi_value": 0,
|
||
|
+ "name": "disk"
|
||
|
+ },
|
||
|
+ "scsi_transport_protocol": {
|
||
|
+ "name": "SAS (SPL-4)",
|
||
|
+ "value": 6
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "temperature_warning": {
|
||
|
+ "enabled": false
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "scsi_percentage_used_endurance_indicator": 0,
|
||
|
+ "temperature": {
|
||
|
+ "current": 30,
|
||
|
+ "drive_trip": 70
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 4665,
|
||
|
+ "minutes": 12
|
||
|
+ },
|
||
|
+ "scsi_start_stop_cycle_counter": {
|
||
|
+ "year_of_manufacture": "2019",
|
||
|
+ "week_of_manufacture": "37"
|
||
|
+ },
|
||
|
+ "scsi_grown_defect_list": 0,
|
||
|
+ "scsi_error_counter_log": {
|
||
|
+ "read": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "2295.821",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "write": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "12103.790",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "verify": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "3.995",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_0": {
|
||
|
+ "code": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "Background long"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 1,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_self_test_1": {
|
||
|
+ "code": {
|
||
|
+ "value": 1,
|
||
|
+ "string": "Background short"
|
||
|
+ },
|
||
|
+ "result": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "Completed"
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 1,
|
||
|
+ "aka": "accumulated_power_on_hours"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_extended_self_test_seconds": 300
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/TOSHIBA_THNSNH128GBST.bin b/tests/smart_dumps/TOSHIBA_THNSNH128GBST.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..8ed22f79
|
||
|
Binary files /dev/null and b/tests/smart_dumps/TOSHIBA_THNSNH128GBST.bin differ
|
||
|
diff --git a/tests/smart_dumps/TOSHIBA_THNSNH128GBST.json b/tests/smart_dumps/TOSHIBA_THNSNH128GBST.json
|
||
|
new file mode 100644
|
||
|
index 00000000..7a6f16c9
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/TOSHIBA_THNSNH128GBST.json
|
||
|
@@ -0,0 +1,544 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-6.0.2-zen",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--info",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "/dev/sda"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5417"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1672926876,
|
||
|
+ "asctime": "Thu Jan 5 14:54:36 2023 CET"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sda",
|
||
|
+ "info_name": "/dev/sda [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_name": "TOSHIBA THNSNH128GBST",
|
||
|
+ "serial_number": "123456789012",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 2061,
|
||
|
+ "id": 1234
|
||
|
+ },
|
||
|
+ "firmware_version": "HTRAN101",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 250069680,
|
||
|
+ "bytes": 128035676160
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 512,
|
||
|
+ "rotation_rate": 0,
|
||
|
+ "form_factor": {
|
||
|
+ "ata_value": 3,
|
||
|
+ "name": "2.5 inches"
|
||
|
+ },
|
||
|
+ "trim": {
|
||
|
+ "supported": true,
|
||
|
+ "deterministic": true,
|
||
|
+ "zeroed": true
|
||
|
+ },
|
||
|
+ "in_smartctl_database": false,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2 (minor revision not indicated)",
|
||
|
+ "major_value": 1016,
|
||
|
+ "minor_value": 0
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.1",
|
||
|
+ "value": 127
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 3,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "was completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "completion_seconds": 120
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 2,
|
||
|
+ "extended": 8
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 91,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": false,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 57,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 10,
|
||
|
+ "string": "-O-R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 2,
|
||
|
+ "name": "Throughput_Performance",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 3,
|
||
|
+ "name": "Spin_Up_Time",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 7,
|
||
|
+ "string": "POS--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 5,
|
||
|
+ "name": "Reallocated_Sector_Ct",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 7,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 11,
|
||
|
+ "string": "PO-R-- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 8,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 5,
|
||
|
+ "string": "P-S--- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 9,
|
||
|
+ "name": "Power_On_Hours",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 29029,
|
||
|
+ "string": "29029"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 10,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 12,
|
||
|
+ "name": "Power_Cycle_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 2322,
|
||
|
+ "string": "2322"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 167,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 34,
|
||
|
+ "string": "-O---K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 168,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 9,
|
||
|
+ "string": "9"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 169,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 10,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 100,
|
||
|
+ "string": "100"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 173,
|
||
|
+ "name": "Unknown_Attribute",
|
||
|
+ "value": 195,
|
||
|
+ "worst": 195,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 175,
|
||
|
+ "name": "Program_Fail_Count_Chip",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 10,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 192,
|
||
|
+ "name": "Power-Off_Retract_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 278,
|
||
|
+ "string": "278"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 194,
|
||
|
+ "name": "Temperature_Celsius",
|
||
|
+ "value": 68,
|
||
|
+ "worst": 52,
|
||
|
+ "thresh": 20,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 35,
|
||
|
+ "string": "PO---K ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 206159216672,
|
||
|
+ "string": "32 (Min/Max 12/48)"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 197,
|
||
|
+ "name": "Current_Pending_Sector",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 18,
|
||
|
+ "string": "-O--C- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 240,
|
||
|
+ "name": "Unknown_SSD_Attribute",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 50,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 19,
|
||
|
+ "string": "PO--C- ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 29029
|
||
|
+ },
|
||
|
+ "power_cycle_count": 2322,
|
||
|
+ "temperature": {
|
||
|
+ "current": 32
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/WD4001FYYG-01SL3.json b/tests/smart_dumps/WD4001FYYG-01SL3.json
|
||
|
new file mode 100644
|
||
|
index 00000000..73755460
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/WD4001FYYG-01SL3.json
|
||
|
@@ -0,0 +1,119 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.19.11-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--info",
|
||
|
+ "--health",
|
||
|
+ "--attributes",
|
||
|
+ "--log=error",
|
||
|
+ "--log=background",
|
||
|
+ "--json",
|
||
|
+ "--device=scsi",
|
||
|
+ "/dev/sdq"
|
||
|
+ ],
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1674653346,
|
||
|
+ "asctime": "Wed Jan 25 08:29:06 2023 EST"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdq",
|
||
|
+ "info_name": "/dev/sdq",
|
||
|
+ "type": "scsi",
|
||
|
+ "protocol": "SCSI"
|
||
|
+ },
|
||
|
+ "scsi_vendor": "WD",
|
||
|
+ "scsi_product": "WD4001FYYG-01SL3",
|
||
|
+ "scsi_model_name": "WD WD4001FYYG-01SL3",
|
||
|
+ "scsi_revision": "VR02",
|
||
|
+ "scsi_version": "SPC-4",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 7814037168,
|
||
|
+ "bytes": 4000787030016
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "rotation_rate": 7200,
|
||
|
+ "form_factor": {
|
||
|
+ "scsi_value": 2,
|
||
|
+ "name": "3.5 inches"
|
||
|
+ },
|
||
|
+ "logical_unit_id": "0x50014eeeeeeeeeee",
|
||
|
+ "serial_number": "WMC1F0123456",
|
||
|
+ "device_type": {
|
||
|
+ "scsi_terminology": "Peripheral Device Type [PDT]",
|
||
|
+ "scsi_value": 0,
|
||
|
+ "name": "disk"
|
||
|
+ },
|
||
|
+ "scsi_transport_protocol": {
|
||
|
+ "name": "SAS (SPL-4)",
|
||
|
+ "value": 6
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": false
|
||
|
+ },
|
||
|
+ "temperature_warning": {
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "temperature": {
|
||
|
+ "current": 29,
|
||
|
+ "drive_trip": 69
|
||
|
+ },
|
||
|
+ "scsi_start_stop_cycle_counter": {
|
||
|
+ "year_of_manufacture": "2012",
|
||
|
+ "week_of_manufacture": "01",
|
||
|
+ "specified_cycle_count_over_device_lifetime": 1048576,
|
||
|
+ "accumulated_start_stop_cycles": 13,
|
||
|
+ "specified_load_unload_count_over_device_lifetime": 1114112,
|
||
|
+ "accumulated_load_unload_cycles": 1
|
||
|
+ },
|
||
|
+ "scsi_grown_defect_list": 0,
|
||
|
+ "scsi_error_counter_log": {
|
||
|
+ "read": {
|
||
|
+ "errors_corrected_by_eccfast": 2,
|
||
|
+ "errors_corrected_by_eccdelayed": 6,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 117,
|
||
|
+ "total_errors_corrected": 8,
|
||
|
+ "correction_algorithm_invocations": 6,
|
||
|
+ "gigabytes_processed": "491.518",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ },
|
||
|
+ "write": {
|
||
|
+ "errors_corrected_by_eccfast": 0,
|
||
|
+ "errors_corrected_by_eccdelayed": 0,
|
||
|
+ "errors_corrected_by_rereads_rewrites": 0,
|
||
|
+ "total_errors_corrected": 0,
|
||
|
+ "correction_algorithm_invocations": 0,
|
||
|
+ "gigabytes_processed": "43.026",
|
||
|
+ "total_uncorrected_errors": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "scsi_background_scan": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "no scans active",
|
||
|
+ "number_scans_performed": 0,
|
||
|
+ "scan_progress": "0.00%",
|
||
|
+ "number_medium_scans_performed": 0
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 63318,
|
||
|
+ "minutes": 32
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/WDC_WD10EFRX-68PJCN0.json b/tests/smart_dumps/WDC_WD10EFRX-68PJCN0.json
|
||
|
new file mode 100644
|
||
|
index 00000000..3320aab3
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_dumps/WDC_WD10EFRX-68PJCN0.json
|
||
|
@@ -0,0 +1,518 @@
|
||
|
+{
|
||
|
+ "json_format_version": [
|
||
|
+ 1,
|
||
|
+ 0
|
||
|
+ ],
|
||
|
+ "smartctl": {
|
||
|
+ "version": [
|
||
|
+ 7,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "svn_revision": "5338",
|
||
|
+ "platform_info": "x86_64-linux-5.19.11-200.fc36.x86_64",
|
||
|
+ "build_info": "(local build)",
|
||
|
+ "argv": [
|
||
|
+ "smartctl",
|
||
|
+ "--info",
|
||
|
+ "--health",
|
||
|
+ "--capabilities",
|
||
|
+ "--attributes",
|
||
|
+ "--json",
|
||
|
+ "--nocheck=never",
|
||
|
+ "--badsum=ignore",
|
||
|
+ "/dev/sdz"
|
||
|
+ ],
|
||
|
+ "drive_database_version": {
|
||
|
+ "string": "7.3/5319"
|
||
|
+ },
|
||
|
+ "exit_status": 0
|
||
|
+ },
|
||
|
+ "local_time": {
|
||
|
+ "time_t": 1674654486,
|
||
|
+ "asctime": "Wed Jan 25 08:48:06 2023 EST"
|
||
|
+ },
|
||
|
+ "device": {
|
||
|
+ "name": "/dev/sdz",
|
||
|
+ "info_name": "/dev/sdz [SAT]",
|
||
|
+ "type": "sat",
|
||
|
+ "protocol": "ATA"
|
||
|
+ },
|
||
|
+ "model_family": "Western Digital Red",
|
||
|
+ "model_name": "WDC WD10EFRX-68PJCN0",
|
||
|
+ "serial_number": "WD-WCC4JABCDESZ",
|
||
|
+ "wwn": {
|
||
|
+ "naa": 5,
|
||
|
+ "oui": 5358,
|
||
|
+ "id": 1234567890
|
||
|
+ },
|
||
|
+ "firmware_version": "82.00A82",
|
||
|
+ "user_capacity": {
|
||
|
+ "blocks": 1953525168,
|
||
|
+ "bytes": 1000204886016
|
||
|
+ },
|
||
|
+ "logical_block_size": 512,
|
||
|
+ "physical_block_size": 4096,
|
||
|
+ "rotation_rate": 5400,
|
||
|
+ "trim": {
|
||
|
+ "supported": false
|
||
|
+ },
|
||
|
+ "in_smartctl_database": true,
|
||
|
+ "ata_version": {
|
||
|
+ "string": "ACS-2 (minor revision not indicated)",
|
||
|
+ "major_value": 1022,
|
||
|
+ "minor_value": 0
|
||
|
+ },
|
||
|
+ "sata_version": {
|
||
|
+ "string": "SATA 3.0",
|
||
|
+ "value": 62
|
||
|
+ },
|
||
|
+ "interface_speed": {
|
||
|
+ "max": {
|
||
|
+ "sata_value": 14,
|
||
|
+ "string": "6.0 Gb/s",
|
||
|
+ "units_per_second": 60,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ },
|
||
|
+ "current": {
|
||
|
+ "sata_value": 1,
|
||
|
+ "string": "1.5 Gb/s",
|
||
|
+ "units_per_second": 15,
|
||
|
+ "bits_per_unit": 100000000
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "smart_support": {
|
||
|
+ "available": true,
|
||
|
+ "enabled": true
|
||
|
+ },
|
||
|
+ "smart_status": {
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "ata_smart_data": {
|
||
|
+ "offline_data_collection": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "was never started"
|
||
|
+ },
|
||
|
+ "completion_seconds": 13140
|
||
|
+ },
|
||
|
+ "self_test": {
|
||
|
+ "status": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "completed without error",
|
||
|
+ "passed": true
|
||
|
+ },
|
||
|
+ "polling_minutes": {
|
||
|
+ "short": 2,
|
||
|
+ "extended": 149,
|
||
|
+ "conveyance": 5
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "capabilities": {
|
||
|
+ "values": [
|
||
|
+ 123,
|
||
|
+ 3
|
||
|
+ ],
|
||
|
+ "exec_offline_immediate_supported": true,
|
||
|
+ "offline_is_aborted_upon_new_cmd": false,
|
||
|
+ "offline_surface_scan_supported": true,
|
||
|
+ "self_tests_supported": true,
|
||
|
+ "conveyance_self_test_supported": true,
|
||
|
+ "selective_self_test_supported": true,
|
||
|
+ "attribute_autosave_enabled": true,
|
||
|
+ "error_logging_supported": true,
|
||
|
+ "gp_logging_supported": true
|
||
|
+ }
|
||
|
+ },
|
||
|
+ "ata_sct_capabilities": {
|
||
|
+ "value": 12349,
|
||
|
+ "error_recovery_control_supported": true,
|
||
|
+ "feature_control_supported": true,
|
||
|
+ "data_table_supported": true
|
||
|
+ },
|
||
|
+ "ata_smart_attributes": {
|
||
|
+ "revision": 16,
|
||
|
+ "table": [
|
||
|
+ {
|
||
|
+ "id": 1,
|
||
|
+ "name": "Raw_Read_Error_Rate",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 51,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 47,
|
||
|
+ "string": "POSR-K ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 3,
|
||
|
+ "name": "Spin_Up_Time",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 253,
|
||
|
+ "thresh": 21,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 39,
|
||
|
+ "string": "POS--K ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 4,
|
||
|
+ "name": "Start_Stop_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 4,
|
||
|
+ "string": "4"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 5,
|
||
|
+ "name": "Reallocated_Sector_Ct",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 140,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 51,
|
||
|
+ "string": "PO--CK ",
|
||
|
+ "prefailure": true,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 7,
|
||
|
+ "name": "Seek_Error_Rate",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 46,
|
||
|
+ "string": "-OSR-K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": true,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 9,
|
||
|
+ "name": "Power_On_Hours",
|
||
|
+ "value": 95,
|
||
|
+ "worst": 94,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 3763,
|
||
|
+ "string": "3763"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 10,
|
||
|
+ "name": "Spin_Retry_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 253,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 11,
|
||
|
+ "name": "Calibration_Retry_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 253,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 12,
|
||
|
+ "name": "Power_Cycle_Count",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 100,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 4,
|
||
|
+ "string": "4"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 192,
|
||
|
+ "name": "Power-Off_Retract_Count",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 2,
|
||
|
+ "string": "2"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 193,
|
||
|
+ "name": "Load_Cycle_Count",
|
||
|
+ "value": 187,
|
||
|
+ "worst": 187,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 40364,
|
||
|
+ "string": "40364"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 194,
|
||
|
+ "name": "Temperature_Celsius",
|
||
|
+ "value": 118,
|
||
|
+ "worst": 104,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 34,
|
||
|
+ "string": "-O---K ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 25,
|
||
|
+ "string": "25"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 196,
|
||
|
+ "name": "Reallocated_Event_Count",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 197,
|
||
|
+ "name": "Current_Pending_Sector",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 198,
|
||
|
+ "name": "Offline_Uncorrectable",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 253,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 48,
|
||
|
+ "string": "----CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 199,
|
||
|
+ "name": "UDMA_CRC_Error_Count",
|
||
|
+ "value": 200,
|
||
|
+ "worst": 200,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 50,
|
||
|
+ "string": "-O--CK ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": true,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": false,
|
||
|
+ "event_count": true,
|
||
|
+ "auto_keep": true
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ },
|
||
|
+ {
|
||
|
+ "id": 200,
|
||
|
+ "name": "Multi_Zone_Error_Rate",
|
||
|
+ "value": 100,
|
||
|
+ "worst": 253,
|
||
|
+ "thresh": 0,
|
||
|
+ "when_failed": "",
|
||
|
+ "flags": {
|
||
|
+ "value": 8,
|
||
|
+ "string": "---R-- ",
|
||
|
+ "prefailure": false,
|
||
|
+ "updated_online": false,
|
||
|
+ "performance": false,
|
||
|
+ "error_rate": true,
|
||
|
+ "event_count": false,
|
||
|
+ "auto_keep": false
|
||
|
+ },
|
||
|
+ "raw": {
|
||
|
+ "value": 0,
|
||
|
+ "string": "0"
|
||
|
+ }
|
||
|
+ }
|
||
|
+ ]
|
||
|
+ },
|
||
|
+ "power_on_time": {
|
||
|
+ "hours": 3763
|
||
|
+ },
|
||
|
+ "power_cycle_count": 4,
|
||
|
+ "temperature": {
|
||
|
+ "current": 25
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/tests/smart_dumps/WDC_WD20EARS-00MVWB0.bin b/tests/smart_dumps/WDC_WD20EARS-00MVWB0.bin
|
||
|
new file mode 100644
|
||
|
index 00000000..c0f3f18d
|
||
|
Binary files /dev/null and b/tests/smart_dumps/WDC_WD20EARS-00MVWB0.bin differ
|
||
|
diff --git a/tests/smart_test.py b/tests/smart_test.py
|
||
|
new file mode 100644
|
||
|
index 00000000..f6d21352
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smart_test.py
|
||
|
@@ -0,0 +1,246 @@
|
||
|
+import unittest
|
||
|
+import os
|
||
|
+import re
|
||
|
+import glob
|
||
|
+import time
|
||
|
+import shutil
|
||
|
+import overrides_hack
|
||
|
+
|
||
|
+from utils import run, create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, TestTags, tag_test, write_file, run_command
|
||
|
+
|
||
|
+import gi
|
||
|
+gi.require_version('GLib', '2.0')
|
||
|
+gi.require_version('BlockDev', '3.0')
|
||
|
+from gi.repository import BlockDev, GLib
|
||
|
+
|
||
|
+
|
||
|
+class SMARTTest(unittest.TestCase):
|
||
|
+
|
||
|
+ # dumps from real drives, both HDD and SSD
|
||
|
+ SKDUMPS = ["TOSHIBA_THNSNH128GBST", "Hitachi_HDS721010CLA632", "WDC_WD20EARS-00MVWB0",
|
||
|
+ "SAMSUNG_HS122JC", "SAMSUNG_MMCRE28G5MXP-0VBH1", "IBM_IC25N020ATCS04-0",
|
||
|
+ "Maxtor_6Y120P0", "SiliconPower_SSD_SBFM61.3", "Patriot_Burst_240GB",
|
||
|
+ "KINGSTON_SA400S37480G_SBFKQ13", "GIGABYTE_GP-GSTFS31100TNTD",
|
||
|
+ "Biwintech_SSD_SX500"]
|
||
|
+
|
||
|
+ @classmethod
|
||
|
+ def setUpClass(cls):
|
||
|
+ cls.ps = BlockDev.PluginSpec(name=BlockDev.Plugin.SMART, so_name="libbd_smart.so.3")
|
||
|
+ cls.ps2 = BlockDev.PluginSpec(name=BlockDev.Plugin.LOOP)
|
||
|
+ if not BlockDev.is_initialized():
|
||
|
+ BlockDev.init([cls.ps, cls.ps2], None)
|
||
|
+ else:
|
||
|
+ BlockDev.reinit([cls.ps, cls.ps2], True, None)
|
||
|
+
|
||
|
+ def _setup_lio(self):
|
||
|
+ self.lio_dev_file = create_sparse_tempfile("smart_test", 1024**3)
|
||
|
+ try:
|
||
|
+ self.lio_dev = create_lio_device(self.lio_dev_file)
|
||
|
+ except RuntimeError as e:
|
||
|
+ raise RuntimeError("Failed to setup LIO device for testing: %s" % e)
|
||
|
+
|
||
|
+ def _clean_lio(self):
|
||
|
+ try:
|
||
|
+ delete_lio_device(self.lio_dev)
|
||
|
+ except:
|
||
|
+ pass
|
||
|
+ os.unlink(self.lio_dev_file)
|
||
|
+
|
||
|
+ def _setup_loop(self):
|
||
|
+ self.loop_dev_file = create_sparse_tempfile("smart_test", 1024**3)
|
||
|
+ succ, self.loop_dev = BlockDev.loop_setup(self.loop_dev_file)
|
||
|
+ self.assertTrue(succ)
|
||
|
+ self.assertTrue(self.loop_dev)
|
||
|
+ self.loop_dev = '/dev/' + self.loop_dev
|
||
|
+
|
||
|
+ def _clean_loop(self):
|
||
|
+ try:
|
||
|
+ BlockDev.loop_teardown(self.loop_dev)
|
||
|
+ except:
|
||
|
+ pass
|
||
|
+ os.unlink(self.loop_dev_file)
|
||
|
+
|
||
|
+ def _setup_scsi_debug(self):
|
||
|
+ res, _out, _err = run_command('modprobe scsi_debug')
|
||
|
+ self.assertEqual(res, 0)
|
||
|
+ dirs = []
|
||
|
+ while len(dirs) < 1:
|
||
|
+ dirs = glob.glob('/sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block')
|
||
|
+ time.sleep(0.1)
|
||
|
+ self.scsi_debug_dev = os.listdir(dirs[0])
|
||
|
+ self.assertEqual(len(self.scsi_debug_dev), 1)
|
||
|
+ self.scsi_debug_dev = '/dev/' + self.scsi_debug_dev[0]
|
||
|
+ self.assertTrue(os.path.exists(self.scsi_debug_dev))
|
||
|
+
|
||
|
+ def _clean_scsi_debug(self):
|
||
|
+ try:
|
||
|
+ device = self.scsi_debug_dev.split('/')[-1]
|
||
|
+ if os.path.exists('/sys/block/' + device):
|
||
|
+ self.write_file('/sys/block/%s/device/delete' % device, '1')
|
||
|
+ while os.path.exists(device):
|
||
|
+ time.sleep(0.1)
|
||
|
+ self.run_command('modprobe -r scsi_debug')
|
||
|
+ except:
|
||
|
+ pass
|
||
|
+
|
||
|
+ @tag_test(TestTags.NOSTORAGE)
|
||
|
+ def test_plugin_version(self):
|
||
|
+ self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SMART), "libbd_smart.so.3")
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_ata_info(self):
|
||
|
+ """Test SMART ATA info on LIO, loop and scsi_debug devices"""
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ msg = r"Error opening device /dev/.*: No such file or directory"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("/dev/nonexistent")
|
||
|
+
|
||
|
+ msg = r"Error reading SMART data from device: Operation not supported"
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ self.addCleanup(self._clean_lio)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.lio_dev)
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.loop_dev)
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.scsi_debug_dev)
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_smart_enable_disable(self):
|
||
|
+ """Test turning SMART functionality on/off over LIO, loop and scsi_debug devices"""
|
||
|
+
|
||
|
+ msg = r"Enabling/disabling ATA SMART functionality is unavailable with libatasmart"
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled("/dev/nonexistent", False)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled("/dev/nonexistent", True)
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ self.addCleanup(self._clean_lio)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, False)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, True)
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, False)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, True)
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, False)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, True)
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_smart_selftest(self):
|
||
|
+ """Test SMART self-test functionality over LIO, loop and scsi_debug devices"""
|
||
|
+
|
||
|
+ tests = [BlockDev.SmartSelfTestOp.OFFLINE, BlockDev.SmartSelfTestOp.SHORT,
|
||
|
+ BlockDev.SmartSelfTestOp.LONG, BlockDev.SmartSelfTestOp.CONVEYANCE,
|
||
|
+ BlockDev.SmartSelfTestOp.ABORT]
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ msg = r"Error opening device /dev/.*: No such file or directory"
|
||
|
+ for t in tests:
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test("/dev/nonexistent", t)
|
||
|
+
|
||
|
+ msg = r"Error triggering device self-test: Operation not supported"
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ self.addCleanup(self._clean_lio)
|
||
|
+ for t in tests:
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.lio_dev, t)
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ for t in tests:
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.loop_dev, t)
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ for t in tests:
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.scsi_debug_dev, t)
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_scsi_info(self):
|
||
|
+ """Test SMART SCSI info on LIO, loop and scsi_debug devices"""
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ msg = r"SCSI SMART is unavailable with libatasmart"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info("/dev/nonexistent")
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.lio_dev)
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.loop_dev)
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.scsi_debug_dev)
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_ata_real_dumps_by_blob(self):
|
||
|
+ """Test SMART ATA info on supplied skdump blobs (from real devices)"""
|
||
|
+
|
||
|
+ # feed it with garbage
|
||
|
+ for d in ["/dev/zero", "/dev/random"]:
|
||
|
+ with open(d, "rb") as f:
|
||
|
+ content = f.read(1024)
|
||
|
+ msg = r"Error parsing blob data: (Unknown error -61|Invalid argument)"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info_from_data(content)
|
||
|
+
|
||
|
+ # feed it with proper skdumps
|
||
|
+ for d in self.SKDUMPS:
|
||
|
+ with open(os.path.join("tests", "smart_dumps", "%s.bin" % d), "rb") as f:
|
||
|
+ content = f.read()
|
||
|
+ data = BlockDev.smart_ata_get_info_from_data(content)
|
||
|
+ self.assertIsNotNone(data)
|
||
|
+ self.assertGreater(data.power_cycle_count, 0)
|
||
|
+ self.assertGreater(data.power_on_time, 0)
|
||
|
+ self.assertEqual(data.smart_capabilities, 0)
|
||
|
+ self.assertTrue(data.smart_enabled)
|
||
|
+ self.assertTrue(data.smart_supported)
|
||
|
+ self.assertLess(data.temperature, 320)
|
||
|
+ self.assertGreater(len(data.attributes), 0)
|
||
|
+ for attr in data.attributes:
|
||
|
+ self.assertGreater(attr.id, 0)
|
||
|
+ self.assertGreater(len(attr.name), 0)
|
||
|
+ self.assertGreater(len(attr.pretty_value_string), 0)
|
||
|
diff --git a/tests/smartmontools_test.py b/tests/smartmontools_test.py
|
||
|
new file mode 100644
|
||
|
index 00000000..67d9481c
|
||
|
--- /dev/null
|
||
|
+++ b/tests/smartmontools_test.py
|
||
|
@@ -0,0 +1,424 @@
|
||
|
+import unittest
|
||
|
+import os
|
||
|
+import re
|
||
|
+import glob
|
||
|
+import time
|
||
|
+import shutil
|
||
|
+import overrides_hack
|
||
|
+
|
||
|
+from utils import run, create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, TestTags, tag_test, write_file, run_command
|
||
|
+
|
||
|
+import gi
|
||
|
+gi.require_version('GLib', '2.0')
|
||
|
+gi.require_version('BlockDev', '3.0')
|
||
|
+from gi.repository import BlockDev, GLib
|
||
|
+
|
||
|
+
|
||
|
+class SmartmontoolsTest(unittest.TestCase):
|
||
|
+
|
||
|
+ # dumps from real drives, both HDD and SSD
|
||
|
+ ATA_JSON_DUMPS = ["TOSHIBA_THNSNH128GBST", "Hitachi_HDS5C3020ALA632", "HGST_HMS5C4040BLE640",
|
||
|
+ "HGST_HUS726060ALA640", "INTEL_SSDSC2BB120G4L", "WDC_WD10EFRX-68PJCN0"]
|
||
|
+ SCSI_JSON_DUMPS = ["WD4001FYYG-01SL3", "HGST_HUSMR3280ASS200", "SEAGATE_ST600MP0036",
|
||
|
+ "TOSHIBA_AL15SEB120NY", "TOSHIBA_AL15SEB18EQY", "TOSHIBA_KPM5XMUG400G"]
|
||
|
+
|
||
|
+ @classmethod
|
||
|
+ def setUpClass(cls):
|
||
|
+ cls.ps = BlockDev.PluginSpec(name=BlockDev.Plugin.SMART, so_name="libbd_smartmontools.so.3")
|
||
|
+ cls.ps2 = BlockDev.PluginSpec(name=BlockDev.Plugin.LOOP)
|
||
|
+ if not BlockDev.is_initialized():
|
||
|
+ BlockDev.init([cls.ps, cls.ps2], None)
|
||
|
+ else:
|
||
|
+ BlockDev.reinit([cls.ps, cls.ps2], True, None)
|
||
|
+
|
||
|
+ def _setup_lio(self):
|
||
|
+ self.lio_dev_file = create_sparse_tempfile("smart_test", 1024**3)
|
||
|
+ try:
|
||
|
+ self.lio_dev = create_lio_device(self.lio_dev_file)
|
||
|
+ except RuntimeError as e:
|
||
|
+ raise RuntimeError("Failed to setup LIO device for testing: %s" % e)
|
||
|
+
|
||
|
+ def _clean_lio(self):
|
||
|
+ try:
|
||
|
+ delete_lio_device(self.lio_dev)
|
||
|
+ except:
|
||
|
+ pass
|
||
|
+ os.unlink(self.lio_dev_file)
|
||
|
+
|
||
|
+ def _setup_loop(self):
|
||
|
+ self.loop_dev_file = create_sparse_tempfile("smart_test", 1024**3)
|
||
|
+ succ, self.loop_dev = BlockDev.loop_setup(self.loop_dev_file)
|
||
|
+ self.assertTrue(succ)
|
||
|
+ self.assertTrue(self.loop_dev)
|
||
|
+ self.loop_dev = '/dev/' + self.loop_dev
|
||
|
+
|
||
|
+ def _clean_loop(self):
|
||
|
+ try:
|
||
|
+ BlockDev.loop_teardown(self.loop_dev)
|
||
|
+ except:
|
||
|
+ pass
|
||
|
+ os.unlink(self.loop_dev_file)
|
||
|
+
|
||
|
+ def _setup_scsi_debug(self):
|
||
|
+ res, _out, _err = run_command('modprobe scsi_debug')
|
||
|
+ self.assertEqual(res, 0)
|
||
|
+ dirs = []
|
||
|
+ while len(dirs) < 1:
|
||
|
+ dirs = glob.glob('/sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block')
|
||
|
+ time.sleep(0.1)
|
||
|
+ self.scsi_debug_dev = os.listdir(dirs[0])
|
||
|
+ self.assertEqual(len(self.scsi_debug_dev), 1)
|
||
|
+ self.scsi_debug_dev = '/dev/' + self.scsi_debug_dev[0]
|
||
|
+ self.assertTrue(os.path.exists(self.scsi_debug_dev))
|
||
|
+
|
||
|
+ def _clean_scsi_debug(self):
|
||
|
+ try:
|
||
|
+ device = self.scsi_debug_dev.split('/')[-1]
|
||
|
+ if os.path.exists('/sys/block/' + device):
|
||
|
+ self.write_file('/sys/block/%s/device/delete' % device, '1')
|
||
|
+ while os.path.exists(device):
|
||
|
+ time.sleep(0.1)
|
||
|
+ self.run_command('modprobe -r scsi_debug')
|
||
|
+ except:
|
||
|
+ pass
|
||
|
+
|
||
|
+ @tag_test(TestTags.NOSTORAGE)
|
||
|
+ def test_plugin_version(self):
|
||
|
+ self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SMART), "libbd_smartmontools.so.3")
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_ata_info(self):
|
||
|
+ """Test SMART ATA info on LIO, loop and scsi_debug devices"""
|
||
|
+
|
||
|
+ if not shutil.which("smartctl"):
|
||
|
+ raise unittest.SkipTest("smartctl executable not found in $PATH, skipping.")
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ msg = r".*Error getting ATA SMART info: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("/dev/nonexistent", None)
|
||
|
+ msg = r".*Error getting ATA SMART info: /dev/.*: Unknown device type 'xxx'"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("/dev/nonexistent", [BlockDev.ExtraArg.new("--device=xxx", "")])
|
||
|
+ msg = r".*Error getting ATA SMART info: Smartctl open device: /dev/.* failed: No such device"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("/dev/nonexistent", [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ self.addCleanup(self._clean_lio)
|
||
|
+ msg = r"Error parsing smartctl JSON data: The member .ata_smart_data. is not defined in the object at the current position."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.lio_dev, None)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.lio_dev, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error getting ATA SMART info: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.lio_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+ msg = r"Error getting ATA SMART info: Read NVMe Identify Controller failed: NVME_IOCTL_ADMIN_CMD: Invalid argument"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.lio_dev, [BlockDev.ExtraArg.new("--device=nvme", "")])
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ msg = r".*Error getting ATA SMART info: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.loop_dev, None)
|
||
|
+ msg = r"Error getting ATA SMART info: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.loop_dev, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.loop_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ msg = r"Error parsing smartctl JSON data: The member .ata_smart_data. is not defined in the object at the current position."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.scsi_debug_dev, None)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.scsi_debug_dev, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error getting ATA SMART info: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info(self.scsi_debug_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_ata_real_dumps_fake_tool(self):
|
||
|
+ """Test SMART ATA info on supplied JSON dumps (from real devices) via fake smartctl"""
|
||
|
+
|
||
|
+ with fake_utils("tests/fake_utils/smartctl"):
|
||
|
+ for d in self.ATA_JSON_DUMPS:
|
||
|
+ data = BlockDev.smart_ata_get_info(d)
|
||
|
+ self.assertIsNotNone(data)
|
||
|
+ self.assertTrue(data.overall_status_passed)
|
||
|
+ self.assertGreater(data.power_cycle_count, 0)
|
||
|
+ self.assertGreater(data.power_on_time, 0)
|
||
|
+ self.assertEqual(data.self_test_percent_remaining, 0)
|
||
|
+ self.assertGreater(data.smart_capabilities, 0)
|
||
|
+ self.assertTrue(data.smart_enabled)
|
||
|
+ self.assertTrue(data.smart_supported)
|
||
|
+ self.assertGreater(data.temperature, 0)
|
||
|
+ self.assertGreater(len(data.attributes), 0)
|
||
|
+ for attr in data.attributes:
|
||
|
+ self.assertGreater(attr.id, 0)
|
||
|
+ self.assertGreater(len(attr.name), 0)
|
||
|
+ self.assertGreater(len(attr.pretty_value_string), 0)
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_ata_real_dumps_by_blob(self):
|
||
|
+ """Test SMART ATA info on supplied skdump blobs (from real devices)"""
|
||
|
+
|
||
|
+ # feed it with garbage
|
||
|
+ for d in ["/dev/zero", "/dev/random"]:
|
||
|
+ with open(d, "rb") as f:
|
||
|
+ content = f.read(1024)
|
||
|
+ msg = r"Error getting ATA SMART info: (Empty response|JSON data must be UTF-8 encoded|.*unexpected character.*)"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info_from_data(content)
|
||
|
+
|
||
|
+ # feed it with proper JSON
|
||
|
+ for d in self.ATA_JSON_DUMPS:
|
||
|
+ with open(os.path.join("tests", "smart_dumps", "%s.json" % d), "rb") as f:
|
||
|
+ content = f.read()
|
||
|
+ data = BlockDev.smart_ata_get_info_from_data(content)
|
||
|
+ self.assertIsNotNone(data)
|
||
|
+ self.assertTrue(data.overall_status_passed)
|
||
|
+ self.assertGreater(data.power_cycle_count, 0)
|
||
|
+ self.assertGreater(data.power_on_time, 0)
|
||
|
+ self.assertEqual(data.self_test_percent_remaining, 0)
|
||
|
+ self.assertGreater(data.smart_capabilities, 0)
|
||
|
+ self.assertTrue(data.smart_enabled)
|
||
|
+ self.assertTrue(data.smart_supported)
|
||
|
+ self.assertGreater(data.temperature, 0)
|
||
|
+ self.assertGreater(len(data.attributes), 0)
|
||
|
+ for attr in data.attributes:
|
||
|
+ self.assertGreater(attr.id, 0)
|
||
|
+ self.assertGreater(len(attr.name), 0)
|
||
|
+ self.assertGreater(len(attr.pretty_value_string), 0)
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_ata_error_dumps(self):
|
||
|
+ """Test SMART ATA info on supplied JSON dumps (error cases)"""
|
||
|
+
|
||
|
+ with fake_utils("tests/fake_utils/smartctl"):
|
||
|
+ msg = r"Reported smartctl JSON format version too low: 0"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("01_old_ver")
|
||
|
+
|
||
|
+ msg = r"Error getting ATA SMART info: Command line did not parse."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("02_exit_err")
|
||
|
+
|
||
|
+ data = BlockDev.smart_ata_get_info("03_exit_err_32")
|
||
|
+ self.assertIsNotNone(data)
|
||
|
+
|
||
|
+ msg = r"Error getting ATA SMART info: .* Parse error: unexpected character"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("04_malformed")
|
||
|
+
|
||
|
+ msg = r"Empty response"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_ata_get_info("05_empty")
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_smart_enable_disable(self):
|
||
|
+ """Test turning SMART functionality on/off over LIO, loop and scsi_debug devices"""
|
||
|
+ # FYI: take this test with a grain of salt - smartctl behaves unpredictably on unsupported
|
||
|
+ # devices, often not reporting any error at all despite the device clearly not supporting
|
||
|
+ # the SMART functionality
|
||
|
+
|
||
|
+ if not shutil.which("smartctl"):
|
||
|
+ raise unittest.SkipTest("smartctl executable not found in $PATH, skipping.")
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ msg = r"Error setting SMART functionality: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled("/dev/nonexistent", False)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled("/dev/nonexistent", True)
|
||
|
+ msg = r"Error setting SMART functionality: Smartctl open device: /dev/.* failed: No such device"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled("/dev/nonexistent", False, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled("/dev/nonexistent", True, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ self.addCleanup(self._clean_lio)
|
||
|
+ msg = r"Error setting SMART functionality: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, False)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, False, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ # smartctl doesn't report failure when turning SMART on
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, True)
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, True, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error setting SMART functionality: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, False, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.lio_dev, True, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ # Sadly there's no specific message reported for loop devices (not being an ATA device)
|
||
|
+ msg = r"Error setting SMART functionality: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, False)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, True)
|
||
|
+ msg = r"Error setting SMART functionality: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, False, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, True, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, False, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.loop_dev, True, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, False)
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, True)
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, False, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, True, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error setting SMART functionality: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, False, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_set_enabled(self.scsi_debug_dev, True, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_smart_selftest(self):
|
||
|
+ """Test SMART self-test functionality over LIO, loop and scsi_debug devices"""
|
||
|
+
|
||
|
+ if not shutil.which("smartctl"):
|
||
|
+ raise unittest.SkipTest("smartctl executable not found in $PATH, skipping.")
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ for t in [BlockDev.SmartSelfTestOp.OFFLINE, BlockDev.SmartSelfTestOp.SHORT,
|
||
|
+ BlockDev.SmartSelfTestOp.LONG, BlockDev.SmartSelfTestOp.CONVEYANCE,
|
||
|
+ BlockDev.SmartSelfTestOp.ABORT]:
|
||
|
+ msg = r"Error executing SMART self-test: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test("/dev/nonexistent", t)
|
||
|
+ msg = r"Error executing SMART self-test: /dev/.*: Unknown device type 'xxx'"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test("/dev/nonexistent", t, [BlockDev.ExtraArg.new("--device=xxx", "")])
|
||
|
+ msg = r"Error executing SMART self-test: Smartctl open device: /dev/.* failed: No such device"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test("/dev/nonexistent", t, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ self.addCleanup(self._clean_lio)
|
||
|
+ for t in [BlockDev.SmartSelfTestOp.OFFLINE, BlockDev.SmartSelfTestOp.SHORT,
|
||
|
+ BlockDev.SmartSelfTestOp.LONG, BlockDev.SmartSelfTestOp.ABORT]:
|
||
|
+ msg = r"Error executing SMART self-test: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.lio_dev, t)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.lio_dev, t, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error executing SMART self-test: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.lio_dev, t, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+ BlockDev.smart_device_self_test(self.lio_dev, BlockDev.SmartSelfTestOp.CONVEYANCE)
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ for t in [BlockDev.SmartSelfTestOp.OFFLINE, BlockDev.SmartSelfTestOp.SHORT,
|
||
|
+ BlockDev.SmartSelfTestOp.LONG, BlockDev.SmartSelfTestOp.CONVEYANCE,
|
||
|
+ BlockDev.SmartSelfTestOp.ABORT]:
|
||
|
+ msg = r"Error executing SMART self-test: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.loop_dev, t)
|
||
|
+ msg = r"Error executing SMART self-test: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.loop_dev, t, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.loop_dev, t, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ for t in [BlockDev.SmartSelfTestOp.OFFLINE, BlockDev.SmartSelfTestOp.SHORT,
|
||
|
+ BlockDev.SmartSelfTestOp.LONG, BlockDev.SmartSelfTestOp.CONVEYANCE,
|
||
|
+ BlockDev.SmartSelfTestOp.ABORT]:
|
||
|
+ BlockDev.smart_device_self_test(self.scsi_debug_dev, t)
|
||
|
+ BlockDev.smart_device_self_test(self.scsi_debug_dev, t, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error executing SMART self-test: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_device_self_test(self.scsi_debug_dev, t, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_scsi_info(self):
|
||
|
+ """Test SMART SCSI info on LIO, loop and scsi_debug devices"""
|
||
|
+
|
||
|
+ if not shutil.which("smartctl"):
|
||
|
+ raise unittest.SkipTest("smartctl executable not found in $PATH, skipping.")
|
||
|
+
|
||
|
+ # non-existing device
|
||
|
+ msg = r"Error getting SCSI SMART info: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info("/dev/nonexistent", None)
|
||
|
+ msg = r"Error getting SCSI SMART info: Smartctl open device: /dev/.* failed: No such device"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info("/dev/nonexistent", [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info("/dev/nonexistent", [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # LIO device (SCSI)
|
||
|
+ self._setup_lio()
|
||
|
+ msg = r"Error getting SCSI SMART info: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.lio_dev, None)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.lio_dev, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error getting SCSI SMART info: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.lio_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # loop device
|
||
|
+ self._setup_loop()
|
||
|
+ self.addCleanup(self._clean_loop)
|
||
|
+ msg = r"Error getting SCSI SMART info: /dev/.*: Unable to detect device type"
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.loop_dev, None)
|
||
|
+ msg = r"Error getting SCSI SMART info: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.loop_dev, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.loop_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ # scsi_debug
|
||
|
+ self._setup_scsi_debug()
|
||
|
+ self.addCleanup(self._clean_scsi_debug)
|
||
|
+ msg = r"Error getting SCSI SMART info: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.scsi_debug_dev, None)
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.scsi_debug_dev, [BlockDev.ExtraArg.new("--device=scsi", "")])
|
||
|
+ msg = r"Error getting SCSI SMART info: Device open failed or device did not return an IDENTIFY DEVICE structure."
|
||
|
+ with self.assertRaisesRegex(GLib.GError, msg):
|
||
|
+ BlockDev.smart_scsi_get_info(self.scsi_debug_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
|
||
|
+
|
||
|
+ @tag_test(TestTags.CORE)
|
||
|
+ def test_scsi_real_dumps(self):
|
||
|
+ """Test SMART SCSI info on supplied JSON dumps (from real devices)"""
|
||
|
+
|
||
|
+ with fake_utils("tests/fake_utils/smartctl"):
|
||
|
+ for d in self.SCSI_JSON_DUMPS:
|
||
|
+ data = BlockDev.smart_scsi_get_info(d)
|
||
|
+ self.assertIsNotNone(data)
|
||
|
+ self.assertTrue(data.overall_status_passed)
|
||
|
+ self.assertTrue(data.smart_supported)
|
||
|
+ self.assertGreater(data.power_on_time, 0)
|
||
|
+ self.assertGreater(data.temperature, 0)
|
||
|
+ self.assertGreater(data.temperature_drive_trip, 0)
|
||
|
+ self.assertGreater(data.read_processed_bytes, 1000000)
|
||
|
+ self.assertGreater(data.write_processed_bytes, 1000000)
|
||
|
diff --git a/tests/utils_test.py b/tests/utils_test.py
|
||
|
index f2a7ae23..e5476823 100644
|
||
|
--- a/tests/utils_test.py
|
||
|
+++ b/tests/utils_test.py
|
||
|
@@ -78,9 +78,16 @@ class UtilsExecLoggingTest(UtilsTestCase):
|
||
|
succ = BlockDev.utils_exec_and_report_error(["true"])
|
||
|
self.assertTrue(succ)
|
||
|
|
||
|
+ succ = BlockDev.utils_exec_and_capture_output_no_progress(["true"])
|
||
|
+ self.assertTrue(succ)
|
||
|
+
|
||
|
with self.assertRaisesRegex(GLib.GError, r"Process reported exit code 1"):
|
||
|
succ = BlockDev.utils_exec_and_report_error(["/bin/false"])
|
||
|
|
||
|
+ succ, out, stderr, status = BlockDev.utils_exec_and_capture_output_no_progress(["/bin/false"])
|
||
|
+ self.assertTrue(succ)
|
||
|
+ self.assertEqual(status, 1)
|
||
|
+
|
||
|
succ, out = BlockDev.utils_exec_and_capture_output(["echo", "hi"])
|
||
|
self.assertTrue(succ)
|
||
|
self.assertEqual(out, "hi\n")
|
||
|
@@ -247,6 +254,15 @@ class UtilsExecLoggingTest(UtilsTestCase):
|
||
|
self.assertTrue(succ)
|
||
|
self.assertGreater(len(out), cnt)
|
||
|
|
||
|
+ (succ, out, stderr, status) = BlockDev.utils_exec_and_capture_output_no_progress(["bash", "-c", r"for i in {1..%d}; do echo -n .; echo -n \# >&2; if [ $(($i%%500)) -eq 0 ]; then echo ''; echo '' >&2; fi; done" % cnt])
|
||
|
+ self.assertTrue(succ)
|
||
|
+ self.assertGreater(len(out), cnt)
|
||
|
+
|
||
|
+ (succ, out, stderr, status) = BlockDev.utils_exec_and_capture_output_no_progress(["bash", "-c", r"for i in {1..%d}; do echo -n . >&2; echo -n \# >&2; if [ $(($i%%500)) -eq 0 ]; then echo '' >&2; fi; done; exit 123" % cnt])
|
||
|
+ self.assertTrue(succ)
|
||
|
+ self.assertEqual(status, 123)
|
||
|
+ self.assertEqual(len(out), 0)
|
||
|
+ self.assertGreater(len(stderr), cnt)
|
||
|
|
||
|
EXEC_PROGRESS_MSG = "Aloha, I'm the progress line you should match."
|
||
|
|
||
|
--
|
||
|
2.45.2
|
||
|
|