From 226f8b9a9fd5cf32cfeeb6d07f87105350c427b6 Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Wed, 30 Oct 2024 10:01:12 +0100 Subject: [PATCH] Update to 3.2.0 Resolves: RHEL-60272 Resolves: RHEL-50820 --- .gitignore | 1 + 0001-misc-RHEL-10-beta-backport.patch | 107 - ...iases-in-bd_utils_have_kernel_module.patch | 86 - ...-passing-size-for-pvresize-over-DBus.patch | 65 - 0004-Upstream-kernel-VDO-support.patch | 571 - 0005-libext2fs-unused-parameters-fix.patch | 86 - 0006-smart-plugin.patch | 10506 ---------------- ...d_nvme_is_tech_avail-to-the-API-file.patch | 54 - libblockdev.spec | 21 +- sources | 3 +- 10 files changed, 10 insertions(+), 11490 deletions(-) delete mode 100644 0001-misc-RHEL-10-beta-backport.patch delete mode 100644 0002-Check-also-for-aliases-in-bd_utils_have_kernel_module.patch delete mode 100644 0003-Fix-passing-size-for-pvresize-over-DBus.patch delete mode 100644 0004-Upstream-kernel-VDO-support.patch delete mode 100644 0005-libext2fs-unused-parameters-fix.patch delete mode 100644 0006-smart-plugin.patch delete mode 100644 0007-nvme-Add-bd_nvme_is_tech_avail-to-the-API-file.patch diff --git a/.gitignore b/.gitignore index ac2e6b2..717dc58 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ /libblockdev-3.0.4.tar.gz /libblockdev-3.1.0.tar.gz /smart-tests.tar.gz +/libblockdev-3.2.0.tar.gz diff --git a/0001-misc-RHEL-10-beta-backport.patch b/0001-misc-RHEL-10-beta-backport.patch deleted file mode 100644 index cf16980..0000000 --- a/0001-misc-RHEL-10-beta-backport.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 9ca6684291e8a30cce2158285078664f8c771638 Mon Sep 17 00:00:00 2001 -From: guazhang -Date: Wed, 27 Mar 2024 11:53:21 +0800 -Subject: [PATCH 1/3] fixed md_create issue #1013 - ---- - src/python/gi/overrides/BlockDev.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py -index d830e485..25852f66 100644 ---- a/src/python/gi/overrides/BlockDev.py -+++ b/src/python/gi/overrides/BlockDev.py -@@ -1018,7 +1018,7 @@ __all__.append("md_get_superblock_size") - - _md_create = BlockDev.md_create - @override(BlockDev.md_create) --def md_create(device_name, level, disks, spares=0, version=None, bitmap=False, chunk_size=0, extra=None, **kwargs): -+def md_create(device_name, level, disks, spares=0, version=None, bitmap=None, chunk_size=0, extra=None, **kwargs): - extra = _get_extra(extra, kwargs) - return _md_create(device_name, level, disks, spares, version, bitmap, chunk_size, extra) - __all__.append("md_create") --- -2.45.0 - - -From 829cea63351c8d5264573eb3d8953cd0069d2566 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Wed, 3 Apr 2024 15:58:04 +0200 -Subject: [PATCH 2/3] crypto: Fix double free in bd_crypto_luks_remove_key - ---- - src/plugins/crypto.c | 1 - - tests/crypto_test.py | 6 ++++++ - 2 files changed, 6 insertions(+), 1 deletion(-) - -diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c -index 5120ec57..f764593f 100644 ---- a/src/plugins/crypto.c -+++ b/src/plugins/crypto.c -@@ -1480,7 +1480,6 @@ gboolean bd_crypto_luks_remove_key (const gchar *device, BDCryptoKeyslotContext - return FALSE; - } - -- crypt_safe_free (key_buf); - crypt_free (cd); - bd_utils_report_finished (progress_id, "Completed"); - return TRUE; -diff --git a/tests/crypto_test.py b/tests/crypto_test.py -index 6fe15a07..54ec0176 100644 ---- a/tests/crypto_test.py -+++ b/tests/crypto_test.py -@@ -524,6 +524,9 @@ class CryptoTestRemoveKey(CryptoTestCase): - succ = BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx2) - self.assertTrue(succ) - -+ nctx3 = BlockDev.CryptoKeyslotContext(keyfile=self.keyfile) -+ succ = BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx3) -+ - with self.assertRaises(GLib.GError): - wctx = BlockDev.CryptoKeyslotContext(passphrase="wrong-passphrase") - BlockDev.crypto_luks_remove_key(self.loop_dev, wctx) -@@ -534,6 +537,9 @@ class CryptoTestRemoveKey(CryptoTestCase): - succ = BlockDev.crypto_luks_remove_key(self.loop_dev, nctx2) - self.assertTrue(succ) - -+ succ = BlockDev.crypto_luks_remove_key(self.loop_dev, nctx3) -+ self.assertTrue(succ) -+ - @tag_test(TestTags.SLOW) - def test_luks_remove_key(self): - self._remove_key(self._luks_format) --- -2.45.0 - - -From c50d43b4d8e9d10b410265c72d2bbf5372eb41e6 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Tue, 16 Apr 2024 12:02:27 +0200 -Subject: [PATCH 3/3] tests: Fix running tests without ntfsprogs - ---- - tests/fs_tests/generic_test.py | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/tests/fs_tests/generic_test.py b/tests/fs_tests/generic_test.py -index fb890229..df25e25a 100644 ---- a/tests/fs_tests/generic_test.py -+++ b/tests/fs_tests/generic_test.py -@@ -342,8 +342,12 @@ class CanResizeRepairCheckLabel(GenericNoDevTestCase): - self.assertEqual(util, "resize2fs") - - avail, util = BlockDev.fs_can_get_min_size("ntfs") -- self.assertTrue(avail) -- self.assertEqual(util, None) -+ if self.ntfs_avail: -+ self.assertTrue(avail) -+ self.assertEqual(util, None) -+ else: -+ self.assertFalse(avail) -+ self.assertEqual(util, "ntfsresize") - - with self.assertRaises(GLib.GError): - BlockDev.fs_can_get_min_size("xfs") --- -2.45.0 - diff --git a/0002-Check-also-for-aliases-in-bd_utils_have_kernel_module.patch b/0002-Check-also-for-aliases-in-bd_utils_have_kernel_module.patch deleted file mode 100644 index d48bd85..0000000 --- a/0002-Check-also-for-aliases-in-bd_utils_have_kernel_module.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 91c22078e9d369d266c044f949b65ebd611f31b0 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Tue, 21 May 2024 15:42:29 +0200 -Subject: [PATCH] utils: Check also for aliases in bd_utils_have_kernel_module - -We want to return true for ext2/3 which are now supported by ext4 -module. ---- - src/utils/module.c | 12 +++++++++++- - tests/utils_test.py | 22 ++++++++++++++++++++++ - 2 files changed, 33 insertions(+), 1 deletion(-) - -diff --git a/src/utils/module.c b/src/utils/module.c -index 66d02343..71d53b46 100644 ---- a/src/utils/module.c -+++ b/src/utils/module.c -@@ -85,6 +85,7 @@ gboolean bd_utils_have_kernel_module (const gchar *module_name, GError **error) - gint ret = 0; - struct kmod_ctx *ctx = NULL; - struct kmod_module *mod = NULL; -+ struct kmod_list *list = NULL; - gchar *null_config = NULL; - const gchar *path = NULL; - gboolean have_path = FALSE; -@@ -100,21 +101,30 @@ gboolean bd_utils_have_kernel_module (const gchar *module_name, GError **error) - } - set_kmod_logging (ctx); - -- ret = kmod_module_new_from_name (ctx, module_name, &mod); -+ ret = kmod_module_new_from_lookup (ctx, module_name, &list); - if (ret < 0) { - g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL, - "Failed to get the module: %s", strerror_l (-ret, c_locale)); - kmod_unref (ctx); -+ kmod_module_unref_list (list); -+ freelocale (c_locale); -+ return FALSE; -+ } -+ -+ if (list == NULL) { -+ kmod_unref (ctx); - freelocale (c_locale); - return FALSE; - } - -+ mod = kmod_module_get_module (list); - path = kmod_module_get_path (mod); - have_path = (path != NULL) && (g_strcmp0 (path, "") != 0); - if (!have_path) { - builtin = kmod_module_get_initstate (mod) == KMOD_MODULE_BUILTIN; - } - kmod_module_unref (mod); -+ kmod_module_unref_list (list); - kmod_unref (ctx); - freelocale (c_locale); - -diff --git a/tests/utils_test.py b/tests/utils_test.py -index d656d309..372228f8 100644 ---- a/tests/utils_test.py -+++ b/tests/utils_test.py -@@ -484,3 +484,25 @@ def test_initialization(self): - self.assertEqual(ver.major, ver2.major) - self.assertEqual(ver.minor, ver2.minor) - self.assertEqual(ver.micro, ver2.micro) -+ -+ -+class UtilsKernelModuleTest(UtilsTestCase): -+ @tag_test(TestTags.NOSTORAGE, TestTags.CORE) -+ def test_have_kernel_module(self): -+ """ Test checking for kernel modules """ -+ -+ have = BlockDev.utils_have_kernel_module("definitely-not-existing-kernel-module") -+ self.assertFalse(have) -+ -+ # loop should be everywhere, right? -+ have = BlockDev.utils_have_kernel_module("loop") -+ self.assertTrue(have) -+ -+ # lets check some filesystems support and compare with 'modprobe' results -+ for fs in ("ext2", "ext3", "ext4", "xfs", "btrfs"): -+ have_fs = BlockDev.utils_have_kernel_module(fs) -+ ret, _out, _err = run_command("modprobe --dry-run %s" % fs) -+ if ret == 0: -+ self.assertTrue(have_fs) -+ else: -+ self.assertFalse(have_fs) diff --git a/0003-Fix-passing-size-for-pvresize-over-DBus.patch b/0003-Fix-passing-size-for-pvresize-over-DBus.patch deleted file mode 100644 index 5d19946..0000000 --- a/0003-Fix-passing-size-for-pvresize-over-DBus.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 7afe64265c9fc6ac67bfc412e43d00e5a21c867a Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Fri, 22 Mar 2024 14:54:15 +0100 -Subject: [PATCH] lvm-dbus: Fix passing size for pvresize over DBus - -"u" is UINT32, we need to use "t" for UINT64 ---- - src/plugins/lvm-dbus.c | 2 +- - tests/lvm_dbus_tests.py | 6 ++++++ - tests/lvm_test.py | 6 ++++++ - 3 files changed, 13 insertions(+), 1 deletion(-) - -diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c -index 5ecf47f6..ff9eb24b 100644 ---- a/src/plugins/lvm-dbus.c -+++ b/src/plugins/lvm-dbus.c -@@ -1776,7 +1776,7 @@ gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg ** - if (!obj_path) - return FALSE; - -- params = g_variant_new ("(u)", size); -+ params = g_variant_new ("(t)", size); - return call_lvm_method_sync (obj_path, PV_INTF, "ReSize", params, NULL, extra, TRUE, error); - } - -diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py -index 013a29c3..c179a82c 100644 ---- a/tests/lvm_dbus_tests.py -+++ b/tests/lvm_dbus_tests.py -@@ -431,9 +431,15 @@ def test_pvresize(self): - succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None) - self.assertTrue(succ) - -+ info = BlockDev.lvm_pvinfo(self.loop_dev) -+ self.assertEqual(info.pv_size, 200 * 1024**2) -+ - succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**3, None) - self.assertTrue(succ) - -+ info = BlockDev.lvm_pvinfo(self.loop_dev) -+ self.assertEqual(info.pv_size, 200 * 1024**3) -+ - @unittest.skipUnless(lvm_dbus_running, "LVM DBus not running") - class LvmTestPVscan(LvmPVonlyTestCase): - def test_pvscan(self): -diff --git a/tests/lvm_test.py b/tests/lvm_test.py -index e7800d8f..261615e9 100644 ---- a/tests/lvm_test.py -+++ b/tests/lvm_test.py -@@ -434,9 +434,15 @@ def test_pvresize(self): - succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None) - self.assertTrue(succ) - -+ info = BlockDev.lvm_pvinfo(self.loop_dev) -+ self.assertEqual(info.pv_size, 200 * 1024**2) -+ - succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**3, None) - self.assertTrue(succ) - -+ info = BlockDev.lvm_pvinfo(self.loop_dev) -+ self.assertEqual(info.pv_size, 200 * 1024**3) -+ - class LvmTestPVscan(LvmPVonlyTestCase): - def test_pvscan(self): - """Verify that pvscan runs without issues with cache or without""" diff --git a/0004-Upstream-kernel-VDO-support.patch b/0004-Upstream-kernel-VDO-support.patch deleted file mode 100644 index 6a01f76..0000000 --- a/0004-Upstream-kernel-VDO-support.patch +++ /dev/null @@ -1,571 +0,0 @@ -From 5db24c0542a6c7ee44e2d487969ef71170d9cb33 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Tue, 2 Jul 2024 12:30:42 +0200 -Subject: [PATCH 1/2] lvm: Check for dm-vdo instead of kvdo module for VDO - support - -VDO is available in the upstream kernel since 6.9 as dm-vdo so we -should check for it instead of the out-of-tree kvdo module. ---- - src/plugins/lvm-dbus.c | 2 +- - src/plugins/lvm.c | 2 +- - tests/lvm_dbus_tests.py | 4 ++-- - tests/lvm_test.py | 4 ++-- - 4 files changed, 6 insertions(+), 6 deletions(-) - -diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c -index d080b389..0c017306 100644 ---- a/src/plugins/lvm-dbus.c -+++ b/src/plugins/lvm-dbus.c -@@ -321,7 +321,7 @@ static const UtilFeatureDep features[FEATURES_LAST] = { - #define MODULE_DEPS_VDO_MASK (1 << MODULE_DEPS_VDO) - #define MODULE_DEPS_LAST 1 - --static const gchar*const module_deps[MODULE_DEPS_LAST] = { "kvdo" }; -+static const gchar*const module_deps[MODULE_DEPS_LAST] = { "dm-vdo" }; - - /** - * bd_lvm_init: -diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c -index 361a084b..dc7491a7 100644 ---- a/src/plugins/lvm.c -+++ b/src/plugins/lvm.c -@@ -329,7 +329,7 @@ static const UtilFeatureDep features[FEATURES_LAST] = { - #define MODULE_DEPS_VDO_MASK (1 << MODULE_DEPS_VDO) - #define MODULE_DEPS_LAST 1 - --static const gchar*const module_deps[MODULE_DEPS_LAST] = { "kvdo" }; -+static const gchar*const module_deps[MODULE_DEPS_LAST] = { "dm-vdo" }; - - #define UNUSED __attribute__((unused)) - -diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py -index fc270cb5..9f302611 100644 ---- a/tests/lvm_dbus_tests.py -+++ b/tests/lvm_dbus_tests.py -@@ -2029,11 +2029,11 @@ class LVMVDOTest(LVMTestCase): - - @classmethod - def setUpClass(cls): -- if not BlockDev.utils_have_kernel_module("kvdo"): -+ if not BlockDev.utils_have_kernel_module("dm-vdo"): - raise unittest.SkipTest("VDO kernel module not available, skipping.") - - try: -- BlockDev.utils_load_kernel_module("kvdo") -+ BlockDev.utils_load_kernel_module("dm-vdo") - except GLib.GError as e: - if "File exists" not in e.message: - raise unittest.SkipTest("cannot load VDO kernel module, skipping.") -diff --git a/tests/lvm_test.py b/tests/lvm_test.py -index a4c90172..b1d65baf 100644 ---- a/tests/lvm_test.py -+++ b/tests/lvm_test.py -@@ -1933,11 +1933,11 @@ class LVMVDOTest(LVMTestCase): - - @classmethod - def setUpClass(cls): -- if not BlockDev.utils_have_kernel_module("kvdo"): -+ if not BlockDev.utils_have_kernel_module("dm-vdo"): - raise unittest.SkipTest("VDO kernel module not available, skipping.") - - try: -- BlockDev.utils_load_kernel_module("kvdo") -+ BlockDev.utils_load_kernel_module("dm-vdo") - except GLib.GError as e: - if "File exists" not in e.message: - raise unittest.SkipTest("cannot load VDO kernel module, skipping.") --- -2.45.2 - - -From 6cf4de50d385d801c1936fc67ee2902f9c60e04e Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Tue, 2 Jul 2024 12:32:46 +0200 -Subject: [PATCH 2/2] lvm: Get VDO stats from device mapper instead of - /sys/kvdo - -The /sys/kvdo API has been removed so we need to get the full -statistics using libdevmapper now. ---- - configure.ac | 4 + - dist/libblockdev.spec.in | 2 + - src/lib/plugin_apis/lvm.api | 6 +- - src/plugins/Makefile.am | 8 +- - src/plugins/lvm-dbus.c | 18 +-- - src/plugins/lvm.c | 18 +-- - src/plugins/vdo_stats.c | 213 ++++++++++++++++++++++-------------- - tests/lvm_dbus_tests.py | 4 +- - tests/lvm_test.py | 10 +- - 9 files changed, 175 insertions(+), 108 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 02b26e3e..cc875bb4 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -193,6 +193,10 @@ AS_IF([test "x$with_dm" != "xno" -o "x$with_lvm" != "xno" -o "x$with_lvm_dbus" ! - [LIBBLOCKDEV_PKG_CHECK_MODULES([DEVMAPPER], [devmapper >= 1.02.93])], - []) - -+AS_IF([test "x$with_lvm" != "xno" -o "x$with_lvm_dbus" != "xno"], -+ [LIBBLOCKDEV_PKG_CHECK_MODULES([YAML], [yaml-0.1])], -+ []) -+ - AS_IF([test "x$with_part" != "xno"], - [LIBBLOCKDEV_PKG_CHECK_MODULES([FDISK], [fdisk >= 2.31.0])], - []) -diff --git a/dist/libblockdev.spec.in b/dist/libblockdev.spec.in -index 768aa0c0..e8703411 100644 ---- a/dist/libblockdev.spec.in -+++ b/dist/libblockdev.spec.in -@@ -290,6 +290,7 @@ with the libblockdev-loop plugin/library. - %if %{with_lvm} - %package lvm - BuildRequires: device-mapper-devel -+BuildRequires: libyaml-devel - Summary: The LVM plugin for the libblockdev library - Requires: %{name}-utils%{?_isa} = %{version}-%{release} - Requires: lvm2 -@@ -312,6 +313,7 @@ with the libblockdev-lvm plugin/library. - %if %{with_lvm_dbus} - %package lvm-dbus - BuildRequires: device-mapper-devel -+BuildRequires: libyaml-devel - Summary: The LVM plugin for the libblockdev library - Requires: %{name}-utils%{?_isa} = %{version}-%{release} - Requires: lvm2-dbusd >= 2.02.156 -diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api -index 2152aa79..3dd5ea4a 100644 ---- a/src/lib/plugin_apis/lvm.api -+++ b/src/lib/plugin_apis/lvm.api -@@ -1965,10 +1965,10 @@ BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_st - * statistics or %NULL in case of error - * (@error gets populated in those cases) - * -- * Statistics are collected from the values exposed by the kernel `kvdo` module -- * at the `/sys/kvdo//statistics/` path. -+ * Statistics are collected from the values exposed by the kernel `dm-vdo` module. -+ * - * Some of the keys are computed to mimic the information produced by the vdo tools. -- * Please note the contents of the hashtable may vary depending on the actual kvdo module version. -+ * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version. - * - * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY - */ -diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am -index 0195e9e1..71bacb7c 100644 ---- a/src/plugins/Makefile.am -+++ b/src/plugins/Makefile.am -@@ -97,16 +97,16 @@ libbd_loop_la_SOURCES = loop.c loop.h - endif - - if WITH_LVM --libbd_lvm_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) -Wall -Wextra -Werror --libbd_lvm_la_LIBADD = ${builddir}/../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) -+libbd_lvm_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) $(YAML_CFLAGS) -Wall -Wextra -Werror -+libbd_lvm_la_LIBADD = ${builddir}/../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) $(YAML_LIBS) - libbd_lvm_la_LDFLAGS = -L${srcdir}/../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*' - libbd_lvm_la_CPPFLAGS = -I${builddir}/../../include/ - libbd_lvm_la_SOURCES = lvm.c lvm.h check_deps.c check_deps.h dm_logging.c dm_logging.h vdo_stats.c vdo_stats.h - endif - - if WITH_LVM_DBUS --libbd_lvm_dbus_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) -Wall -Wextra -Werror --libbd_lvm_dbus_la_LIBADD = ${builddir}/../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) -+libbd_lvm_dbus_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) $(YAML_CFLAGS) -Wall -Wextra -Werror -+libbd_lvm_dbus_la_LIBADD = ${builddir}/../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) $(YAML_LIBS) - libbd_lvm_dbus_la_LDFLAGS = -L${srcdir}/../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*' - libbd_lvm_dbus_la_CPPFLAGS = -I${builddir}/../../include/ - libbd_lvm_dbus_la_SOURCES = lvm-dbus.c lvm.h check_deps.c check_deps.h dm_logging.c dm_logging.h vdo_stats.c vdo_stats.h -diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c -index 0c017306..92d90718 100644 ---- a/src/plugins/lvm-dbus.c -+++ b/src/plugins/lvm-dbus.c -@@ -4701,10 +4701,10 @@ BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_st - * statistics or %NULL in case of error - * (@error gets populated in those cases) - * -- * Statistics are collected from the values exposed by the kernel `kvdo` module -- * at the `/sys/kvdo//statistics/` path. -+ * Statistics are collected from the values exposed by the kernel `dm-vdo` module. -+ * - * Some of the keys are computed to mimic the information produced by the vdo tools. -- * Please note the contents of the hashtable may vary depending on the actual kvdo module version. -+ * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version. - * - * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY - */ -@@ -4736,12 +4736,12 @@ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_nam - return NULL; - - stats = g_new0 (BDLVMVDOStats, 1); -- get_stat_val64_default (full_stats, "block_size", &stats->block_size, -1); -- get_stat_val64_default (full_stats, "logical_block_size", &stats->logical_block_size, -1); -- get_stat_val64_default (full_stats, "physical_blocks", &stats->physical_blocks, -1); -- get_stat_val64_default (full_stats, "data_blocks_used", &stats->data_blocks_used, -1); -- get_stat_val64_default (full_stats, "overhead_blocks_used", &stats->overhead_blocks_used, -1); -- get_stat_val64_default (full_stats, "logical_blocks_used", &stats->logical_blocks_used, -1); -+ get_stat_val64_default (full_stats, "blockSize", &stats->block_size, -1); -+ get_stat_val64_default (full_stats, "logicalBlockSize", &stats->logical_block_size, -1); -+ get_stat_val64_default (full_stats, "physicalBlocks", &stats->physical_blocks, -1); -+ get_stat_val64_default (full_stats, "dataBlocksUsed", &stats->data_blocks_used, -1); -+ get_stat_val64_default (full_stats, "overheadBlocksUsed", &stats->overhead_blocks_used, -1); -+ get_stat_val64_default (full_stats, "logicalBlocksUsed", &stats->logical_blocks_used, -1); - get_stat_val64_default (full_stats, "usedPercent", &stats->used_percent, -1); - get_stat_val64_default (full_stats, "savingPercent", &stats->saving_percent, -1); - if (!get_stat_val_double (full_stats, "writeAmplificationRatio", &stats->write_amplification_ratio)) -diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c -index dc7491a7..0af9a382 100644 ---- a/src/plugins/lvm.c -+++ b/src/plugins/lvm.c -@@ -3799,10 +3799,10 @@ BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_st - * statistics or %NULL in case of error - * (@error gets populated in those cases) - * -- * Statistics are collected from the values exposed by the kernel `kvdo` module -- * at the `/sys/kvdo//statistics/` path. -+ * Statistics are collected from the values exposed by the kernel `dm-vdo` module. -+ * - * Some of the keys are computed to mimic the information produced by the vdo tools. -- * Please note the contents of the hashtable may vary depending on the actual kvdo module version. -+ * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version. - * - * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY - */ -@@ -3834,12 +3834,12 @@ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_nam - return NULL; - - stats = g_new0 (BDLVMVDOStats, 1); -- get_stat_val64_default (full_stats, "block_size", &stats->block_size, -1); -- get_stat_val64_default (full_stats, "logical_block_size", &stats->logical_block_size, -1); -- get_stat_val64_default (full_stats, "physical_blocks", &stats->physical_blocks, -1); -- get_stat_val64_default (full_stats, "data_blocks_used", &stats->data_blocks_used, -1); -- get_stat_val64_default (full_stats, "overhead_blocks_used", &stats->overhead_blocks_used, -1); -- get_stat_val64_default (full_stats, "logical_blocks_used", &stats->logical_blocks_used, -1); -+ get_stat_val64_default (full_stats, "blockSize", &stats->block_size, -1); -+ get_stat_val64_default (full_stats, "logicalBlockSize", &stats->logical_block_size, -1); -+ get_stat_val64_default (full_stats, "physicalBlocks", &stats->physical_blocks, -1); -+ get_stat_val64_default (full_stats, "dataBlocksUsed", &stats->data_blocks_used, -1); -+ get_stat_val64_default (full_stats, "overheadBlocksUsed", &stats->overhead_blocks_used, -1); -+ get_stat_val64_default (full_stats, "logicalBlocksUsed", &stats->logical_blocks_used, -1); - get_stat_val64_default (full_stats, "usedPercent", &stats->used_percent, -1); - get_stat_val64_default (full_stats, "savingPercent", &stats->saving_percent, -1); - if (!get_stat_val_double (full_stats, "writeAmplificationRatio", &stats->write_amplification_ratio)) -diff --git a/src/plugins/vdo_stats.c b/src/plugins/vdo_stats.c -index 620e972f..ab914821 100644 ---- a/src/plugins/vdo_stats.c -+++ b/src/plugins/vdo_stats.c -@@ -19,10 +19,11 @@ - - #include - #include -+#include -+#include - - #include "vdo_stats.h" -- --#define VDO_SYS_PATH "/sys/kvdo" -+#include "lvm.h" - - - gboolean __attribute__ ((visibility ("hidden"))) -@@ -67,9 +68,9 @@ get_stat_val_double (GHashTable *stats, const gchar *key, gdouble *val) { - static void add_write_ampl_r_stats (GHashTable *stats) { - gint64 bios_meta_write, bios_out_write, bios_in_write; - -- if (! get_stat_val64 (stats, "bios_meta_write", &bios_meta_write) || -- ! get_stat_val64 (stats, "bios_out_write", &bios_out_write) || -- ! get_stat_val64 (stats, "bios_in_write", &bios_in_write)) -+ if (! get_stat_val64 (stats, "biosMetaWrite", &bios_meta_write) || -+ ! get_stat_val64 (stats, "biosOutWrite", &bios_out_write) || -+ ! get_stat_val64 (stats, "biosInWrite", &bios_in_write)) - return; - - if (bios_in_write <= 0) -@@ -84,11 +85,11 @@ static void add_block_stats (GHashTable *stats) { - gint64 physical_blocks, block_size, data_blocks_used, overhead_blocks_used, logical_blocks_used; - gint64 savings; - -- if (! get_stat_val64 (stats, "physical_blocks", &physical_blocks) || -- ! get_stat_val64 (stats, "block_size", &block_size) || -- ! get_stat_val64 (stats, "data_blocks_used", &data_blocks_used) || -- ! get_stat_val64 (stats, "overhead_blocks_used", &overhead_blocks_used) || -- ! get_stat_val64 (stats, "logical_blocks_used", &logical_blocks_used)) -+ if (! get_stat_val64 (stats, "physicalBlocks", &physical_blocks) || -+ ! get_stat_val64 (stats, "blockSize", &block_size) || -+ ! get_stat_val64 (stats, "dataBlocksUsed", &data_blocks_used) || -+ ! get_stat_val64 (stats, "overheadBlocksUsed", &overhead_blocks_used) || -+ ! get_stat_val64 (stats, "logicalBlocksUsed", &logical_blocks_used)) - return; - - g_hash_table_replace (stats, g_strdup ("oneKBlocks"), g_strdup_printf ("%"G_GINT64_FORMAT, physical_blocks * block_size / 1024)); -@@ -105,24 +106,24 @@ static void add_journal_stats (GHashTable *stats) { - gint64 journal_entries_committed, journal_entries_started, journal_entries_written; - gint64 journal_blocks_committed, journal_blocks_started, journal_blocks_written; - -- if (! get_stat_val64 (stats, "journal_entries_committed", &journal_entries_committed) || -- ! get_stat_val64 (stats, "journal_entries_started", &journal_entries_started) || -- ! get_stat_val64 (stats, "journal_entries_written", &journal_entries_written) || -- ! get_stat_val64 (stats, "journal_blocks_committed", &journal_blocks_committed) || -- ! get_stat_val64 (stats, "journal_blocks_started", &journal_blocks_started) || -- ! get_stat_val64 (stats, "journal_blocks_written", &journal_blocks_written)) -+ if (! get_stat_val64 (stats, "journalEntriesCommitted", &journal_entries_committed) || -+ ! get_stat_val64 (stats, "journalEntriesStarted", &journal_entries_started) || -+ ! get_stat_val64 (stats, "journalEntriesWritten", &journal_entries_written) || -+ ! get_stat_val64 (stats, "journalBlocksCommitted", &journal_blocks_committed) || -+ ! get_stat_val64 (stats, "journalBlocksStarted", &journal_blocks_started) || -+ ! get_stat_val64 (stats, "journalBlocksWritten", &journal_blocks_written)) - return; - -- g_hash_table_replace (stats, g_strdup ("journal_entries_batching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_started - journal_entries_written)); -- g_hash_table_replace (stats, g_strdup ("journal_entries_writing"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_written - journal_entries_committed)); -- g_hash_table_replace (stats, g_strdup ("journal_blocks_batching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_started - journal_blocks_written)); -- g_hash_table_replace (stats, g_strdup ("journal_blocks_writing"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_written - journal_blocks_committed)); -+ g_hash_table_replace (stats, g_strdup ("journalEntriesBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_started - journal_entries_written)); -+ g_hash_table_replace (stats, g_strdup ("journalEntriesWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_written - journal_entries_committed)); -+ g_hash_table_replace (stats, g_strdup ("journalBlocksBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_started - journal_blocks_written)); -+ g_hash_table_replace (stats, g_strdup ("journalBlocksWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_written - journal_blocks_committed)); - } - - static void add_computed_stats (GHashTable *stats) { - const gchar *s; - -- s = g_hash_table_lookup (stats, "logical_block_size"); -+ s = g_hash_table_lookup (stats, "logicalBlockSize"); - g_hash_table_replace (stats, - g_strdup ("fiveTwelveByteEmulation"), - g_strdup ((g_strcmp0 (s, "512") == 0) ? "true" : "false")); -@@ -132,76 +133,128 @@ static void add_computed_stats (GHashTable *stats) { - add_journal_stats (stats); - } - --static gchar* _dm_node_from_name (const gchar *map_name, GError **error) { -- gchar *dev_path = NULL; -- gchar *ret = NULL; -- gchar *dev_mapper_path = g_strdup_printf ("/dev/mapper/%s", map_name); -+enum parse_flags { -+ PARSE_NEXT_KEY, -+ PARSE_NEXT_VAL, -+ PARSE_NEXT_IGN, -+}; - -- dev_path = bd_utils_resolve_device (dev_mapper_path, error); -- g_free (dev_mapper_path); -- if (!dev_path) -- /* error is already populated */ -+GHashTable __attribute__ ((visibility ("hidden"))) -+*vdo_get_stats_full (const gchar *name, GError **error) { -+ struct dm_task *dmt = NULL; -+ const gchar *response = NULL; -+ yaml_parser_t parser; -+ yaml_token_t token; -+ GHashTable *stats = NULL; -+ gchar *key = NULL; -+ gsize len = 0; -+ int next_token = PARSE_NEXT_IGN; -+ gchar *prefix = NULL; -+ -+ dmt = dm_task_create (DM_DEVICE_TARGET_MSG); -+ if (!dmt) { -+ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR, -+ "Failed to create DM task"); - return NULL; -+ } - -- ret = g_path_get_basename (dev_path); -- g_free (dev_path); -+ if (!dm_task_set_name (dmt, name)) { -+ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR, -+ "Failed to set name for DM task"); -+ dm_task_destroy (dmt); -+ return NULL; -+ } - -- return ret; --} -+ if (!dm_task_set_message (dmt, "stats")) { -+ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR, -+ "Failed to set message for DM task"); -+ dm_task_destroy (dmt); -+ return NULL; -+ } - --GHashTable __attribute__ ((visibility ("hidden"))) --*vdo_get_stats_full (const gchar *name, GError **error) { -- GHashTable *stats; -- GDir *dir; -- gchar *stats_dir; -- const gchar *direntry; -- gchar *s; -- gchar *val = NULL; -- g_autofree gchar *dm_node = NULL; -- GError *l_error = NULL; -- -- /* try "new" (kvdo >= 8) path first -- /sys/block/dm-X/vdo/statistics */ -- dm_node = _dm_node_from_name (name, error); -- if (dm_node == NULL) { -- g_prefix_error (error, "Failed to get DM node for %s: ", name); -+ if (!dm_task_run (dmt)) { -+ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR, -+ "Failed to run DM task"); -+ dm_task_destroy (dmt); -+ return NULL; -+ } -+ -+ response = dm_task_get_message_response (dmt); -+ if (!response) { -+ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR, -+ "Failed to get response from the DM task"); -+ dm_task_destroy (dmt); - return NULL; - } - -- stats_dir = g_build_path (G_DIR_SEPARATOR_S, "/sys/block", dm_node, "vdo/statistics", NULL); -- dir = g_dir_open (stats_dir, 0, &l_error); -- if (dir == NULL) { -- bd_utils_log_format (BD_UTILS_LOG_INFO, -- "Failed to read VDO stats using the new API, falling back to %s: %s", -- VDO_SYS_PATH, l_error->message); -- g_free (stats_dir); -- g_clear_error (&l_error); -- -- /* lets try /sys/kvdo */ -- stats_dir = g_build_path (G_DIR_SEPARATOR_S, VDO_SYS_PATH, name, "statistics", NULL); -- dir = g_dir_open (stats_dir, 0, error); -- if (dir == NULL) { -- g_prefix_error (error, "Error reading statistics from %s: ", stats_dir); -- g_free (stats_dir); -- return NULL; -- } -+ if (!yaml_parser_initialize (&parser)) { -+ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR, -+ "Failed to get initialize YAML parser"); -+ dm_task_destroy (dmt); -+ return NULL; - } - - stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); -- while ((direntry = g_dir_read_name (dir))) { -- s = g_build_filename (stats_dir, direntry, NULL); -- if (! g_file_get_contents (s, &val, NULL, error)) { -- g_prefix_error (error, "Error reading statistics from %s: ", s); -- g_free (s); -- g_hash_table_destroy (stats); -- stats = NULL; -- break; -- } -- g_hash_table_replace (stats, g_strdup (direntry), g_strdup (g_strstrip (val))); -- g_free (val); -- g_free (s); -- } -- g_dir_close (dir); -- g_free (stats_dir); -+ yaml_parser_set_input_string (&parser, (guchar *) response, strlen (response)); -+ -+ do { -+ yaml_parser_scan (&parser, &token); -+ switch (token.type) { -+ /* key */ -+ case YAML_KEY_TOKEN: -+ next_token = PARSE_NEXT_KEY; -+ break; -+ /* value */ -+ case YAML_VALUE_TOKEN: -+ next_token = PARSE_NEXT_VAL; -+ break; -+ /* block mapping */ -+ case YAML_BLOCK_MAPPING_START_TOKEN: -+ if (next_token == PARSE_NEXT_VAL) -+ /* we were expecting to read a key-value pair but this is actually -+ a block start, so we need to free the key we're not going to use */ -+ g_free (key); -+ break; -+ /* mapping */ -+ case YAML_FLOW_MAPPING_START_TOKEN: -+ /* start of flow mapping -> previously read key will be used as prefix -+ for all keys in the mapping: -+ previous key: biosInProgress -+ keys in the mapping: Read, Write... -+ with prefix: biosInProgressRead, biosInProgressWrite... -+ */ -+ prefix = key; -+ break; -+ case YAML_FLOW_MAPPING_END_TOKEN: -+ /* end of flow mapping, discard the prefix used */ -+ g_free (prefix); -+ prefix = NULL; -+ break; -+ /* actual data */ -+ case YAML_SCALAR_TOKEN: -+ if (next_token == PARSE_NEXT_KEY) { -+ if (prefix) { -+ key = g_strdup_printf ("%s%s", prefix, (const gchar *) token.data.scalar.value); -+ len = strlen (prefix); -+ /* make sure the key with the prefix is still camelCase */ -+ key[len] = g_ascii_toupper (key[len]); -+ } else -+ key = g_strdup ((const gchar *) token.data.scalar.value); -+ } else if (next_token == PARSE_NEXT_VAL) { -+ gchar *val = g_strdup ((const gchar *) token.data.scalar.value); -+ g_hash_table_insert (stats, key, val); -+ } -+ break; -+ default: -+ break; -+ } -+ -+ if (token.type != YAML_STREAM_END_TOKEN) -+ yaml_token_delete (&token); -+ } while (token.type != YAML_STREAM_END_TOKEN); -+ -+ yaml_parser_delete (&parser); -+ dm_task_destroy (dmt); - - if (stats != NULL) - add_computed_stats (stats); -diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py -index 9f302611..db0d5c90 100644 ---- a/tests/lvm_dbus_tests.py -+++ b/tests/lvm_dbus_tests.py -@@ -2090,7 +2090,9 @@ class LVMVDOTest(LVMTestCase): - pool_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool") - self.assertEqual(pool_info.segtype, "vdo-pool") - self.assertEqual(pool_info.data_lv, "vdoPool_vdata") -- self.assertGreater(pool_info.data_percent, 0) -+ lvm_version = self._get_lvm_version() -+ if lvm_version >= Version("2.03.24"): -+ self.assertGreater(pool_info.data_percent, 0) - - pool = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV") - self.assertEqual(pool, lv_info.pool_lv) -diff --git a/tests/lvm_test.py b/tests/lvm_test.py -index b1d65baf..96aa352a 100644 ---- a/tests/lvm_test.py -+++ b/tests/lvm_test.py -@@ -1994,7 +1994,9 @@ class LVMVDOTest(LVMTestCase): - pool_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool") - self.assertEqual(pool_info.segtype, "vdo-pool") - self.assertEqual(pool_info.data_lv, "vdoPool_vdata") -- self.assertGreater(pool_info.data_percent, 0) -+ lvm_version = self._get_lvm_version() -+ if lvm_version >= Version("2.03.24"): -+ self.assertGreater(pool_info.data_percent, 0) - - pool = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV") - self.assertEqual(pool, lv_info.pool_lv) -@@ -2155,7 +2157,11 @@ class LVMVDOTest(LVMTestCase): - self.assertTrue(vdo_info.deduplication) - - vdo_stats = BlockDev.lvm_vdo_get_stats("testVDOVG", "vdoPool") -- self.assertEqual(vdo_info.saving_percent, vdo_stats.saving_percent) -+ -+ lvm_version = self._get_lvm_version() -+ if lvm_version >= Version("2.03.24"): -+ # saving_percent is incorrect with LVM < 2.03.24 -+ self.assertEqual(vdo_info.saving_percent, vdo_stats.saving_percent) - - # just sanity check - self.assertNotEqual(vdo_stats.used_percent, -1) --- -2.45.2 - diff --git a/0005-libext2fs-unused-parameters-fix.patch b/0005-libext2fs-unused-parameters-fix.patch deleted file mode 100644 index cfeb97c..0000000 --- a/0005-libext2fs-unused-parameters-fix.patch +++ /dev/null @@ -1,86 +0,0 @@ -From e1c3c3e3555963104a8de00b510b69375eee5ced Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Fri, 7 Jun 2024 10:06:22 +0200 -Subject: [PATCH 1/3] fs: Ignore unused-parameter warning in the FS plugin - -There are some unused parameters in the libext2fs header which -together with Wall and Werror means the plugin compilation fails. -As a workaround we'll disable the unused-parameter warning for -now. - -Fixes: #1026 ---- - src/plugins/fs/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/plugins/fs/Makefile.am b/src/plugins/fs/Makefile.am -index 7d4849c0..00474d5a 100644 ---- a/src/plugins/fs/Makefile.am -+++ b/src/plugins/fs/Makefile.am -@@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = subdir-objects - - lib_LTLIBRARIES = libbd_fs.la - --libbd_fs_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(BLKID_CFLAGS) $(MOUNT_CFLAGS) $(UUID_CFLAGS) $(EXT2FS_CFLAGS) -Wall -Wextra -Werror -+libbd_fs_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(BLKID_CFLAGS) $(MOUNT_CFLAGS) $(UUID_CFLAGS) $(EXT2FS_CFLAGS) -Wall -Wextra -Werror -Wno-unused-parameter - libbd_fs_la_LIBADD = ${builddir}/../../utils/libbd_utils.la $(GLIB_LIBS) $(GIO_LIBS) $(BLKID_LIBS) $(MOUNT_LIBS) $(UUID_LIBS) $(EXT2FS_LIBS) - libbd_fs_la_LDFLAGS = -L${srcdir}/../../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*' - libbd_fs_la_CPPFLAGS = -I${builddir}/../../../include/ -I${srcdir}/../ --- -2.45.2 - - -From 30e7d0bd7b78b5d9a2ddd031d0cdf9f45187a717 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Fri, 7 Jun 2024 15:21:09 +0200 -Subject: [PATCH 2/3] fs: Ignore shift-count-overflow warning in FS plugin - -The warning happens in the libext2fs header. ---- - src/plugins/fs/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/plugins/fs/Makefile.am b/src/plugins/fs/Makefile.am -index 00474d5a..42e1c777 100644 ---- a/src/plugins/fs/Makefile.am -+++ b/src/plugins/fs/Makefile.am -@@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = subdir-objects - - lib_LTLIBRARIES = libbd_fs.la - --libbd_fs_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(BLKID_CFLAGS) $(MOUNT_CFLAGS) $(UUID_CFLAGS) $(EXT2FS_CFLAGS) -Wall -Wextra -Werror -Wno-unused-parameter -+libbd_fs_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(BLKID_CFLAGS) $(MOUNT_CFLAGS) $(UUID_CFLAGS) $(EXT2FS_CFLAGS) -Wall -Wextra -Werror -Wno-unused-parameter -Wno-shift-count-overflow - libbd_fs_la_LIBADD = ${builddir}/../../utils/libbd_utils.la $(GLIB_LIBS) $(GIO_LIBS) $(BLKID_LIBS) $(MOUNT_LIBS) $(UUID_LIBS) $(EXT2FS_LIBS) - libbd_fs_la_LDFLAGS = -L${srcdir}/../../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*' - libbd_fs_la_CPPFLAGS = -I${builddir}/../../../include/ -I${srcdir}/../ --- -2.45.2 - - -From b40ce052f98c9afb3e5bd0f91e7eb8bf6cbacd90 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Wed, 12 Jun 2024 15:34:02 +0200 -Subject: [PATCH 3/3] fs: Fix ignoring errors from libext2fs - -Follow-up for #1028, we still want to show the warning and not -completely supress it. ---- - src/plugins/fs/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/plugins/fs/Makefile.am b/src/plugins/fs/Makefile.am -index 42e1c777..1ee256f0 100644 ---- a/src/plugins/fs/Makefile.am -+++ b/src/plugins/fs/Makefile.am -@@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = subdir-objects - - lib_LTLIBRARIES = libbd_fs.la - --libbd_fs_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(BLKID_CFLAGS) $(MOUNT_CFLAGS) $(UUID_CFLAGS) $(EXT2FS_CFLAGS) -Wall -Wextra -Werror -Wno-unused-parameter -Wno-shift-count-overflow -+libbd_fs_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(BLKID_CFLAGS) $(MOUNT_CFLAGS) $(UUID_CFLAGS) $(EXT2FS_CFLAGS) -Wall -Wextra -Werror -Wno-error=unused-parameter -Wno-error=shift-count-overflow - libbd_fs_la_LIBADD = ${builddir}/../../utils/libbd_utils.la $(GLIB_LIBS) $(GIO_LIBS) $(BLKID_LIBS) $(MOUNT_LIBS) $(UUID_LIBS) $(EXT2FS_LIBS) - libbd_fs_la_LDFLAGS = -L${srcdir}/../../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*' - libbd_fs_la_CPPFLAGS = -I${builddir}/../../../include/ -I${srcdir}/../ --- -2.45.2 - diff --git a/0006-smart-plugin.patch b/0006-smart-plugin.patch deleted file mode 100644 index 6602c9b..0000000 --- a/0006-smart-plugin.patch +++ /dev/null @@ -1,10506 +0,0 @@ -From 386480674962607c6166c2835f2ae3057800651f Mon Sep 17 00:00:00 2001 -From: Tomas Bzatek -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 -+}; -+])], -+ [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 @@ - - - -+ - - - -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 - -+ -+
-+smart -+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 -+
-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 -+#include -+ -+#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 . -+ * -+ * Author: Tomas Bzatek -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#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 -+}; -+ -+ -+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 . -+ * -+ * Author: Tomas Bzatek -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+#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 . -+ * -+ * Author: Tomas Bzatek -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#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 -+#include -+#include -+ -+#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 -+#include -+#include -+ -+#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 . -+ * -+ * Author: Tomas Bzatek -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+#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 - diff --git a/0007-nvme-Add-bd_nvme_is_tech_avail-to-the-API-file.patch b/0007-nvme-Add-bd_nvme_is_tech_avail-to-the-API-file.patch deleted file mode 100644 index 949cf68..0000000 --- a/0007-nvme-Add-bd_nvme_is_tech_avail-to-the-API-file.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 11c7d2542addb74b62849caa3c8c7057c41fd45f Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Thu, 14 Mar 2024 13:53:04 +0100 -Subject: [PATCH] nvme: Add bd_nvme_is_tech_avail to the API file - ---- - src/lib/plugin_apis/nvme.api | 11 +++++++++++ - tests/nvme_test.py | 8 ++++++++ - 2 files changed, 19 insertions(+) - -diff --git a/src/lib/plugin_apis/nvme.api b/src/lib/plugin_apis/nvme.api -index 44f8bf73..2f17e0c9 100644 ---- a/src/lib/plugin_apis/nvme.api -+++ b/src/lib/plugin_apis/nvme.api -@@ -66,6 +66,17 @@ typedef enum { - BD_NVME_TECH_MODE_INITIATOR = 1 << 2, - } BDNVMETechMode; - -+/** -+ * bd_nvme_is_tech_avail: -+ * @tech: the queried tech -+ * @mode: a bit mask of queried modes of operation (#BDNVMETechMode) 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_nvme_is_tech_avail (BDNVMETech tech, G_GNUC_UNUSED guint64 mode, GError **error); -+ - - /* BpG-skip */ - /** -diff --git a/tests/nvme_test.py b/tests/nvme_test.py -index f31de546..df31f112 100644 ---- a/tests/nvme_test.py -+++ b/tests/nvme_test.py -@@ -44,6 +44,14 @@ class NVMePluginVersionTestCase(NVMeTest): - def test_plugin_version(self): - self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.NVME), "libbd_nvme.so.3") - -+ @tag_test(TestTags.NOSTORAGE) -+ def test_availability(self): -+ avail = BlockDev.nvme_is_tech_avail(BlockDev.NVMETech.NVME, 0) -+ self.assertTrue(avail) -+ -+ avail = BlockDev.nvme_is_tech_avail(BlockDev.NVMETech.FABRICS, 0) -+ self.assertTrue(avail) -+ - - class NVMeTestCase(NVMeTest): - def setUp(self): --- -2.46.2 - diff --git a/libblockdev.spec b/libblockdev.spec index a5567f4..590fc9f 100644 --- a/libblockdev.spec +++ b/libblockdev.spec @@ -85,21 +85,12 @@ %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 -Release: 9%{?dist} +Version: 3.2.0 +Release: 1%{?dist} Summary: A library for low-level manipulation with block devices License: LGPL-2.1-or-later URL: https://github.com/storaged-project/libblockdev -Source0: https://github.com/storaged-project/libblockdev/releases/download/%{version}-%{release}/%{name}-%{version}.tar.gz -Source1: smart-tests.tar.gz - -Patch0: 0001-misc-RHEL-10-beta-backport.patch -Patch1: 0002-Check-also-for-aliases-in-bd_utils_have_kernel_module.patch -Patch2: 0003-Fix-passing-size-for-pvresize-over-DBus.patch -Patch3: 0004-Upstream-kernel-VDO-support.patch -Patch4: 0005-libext2fs-unused-parameters-fix.patch -Patch5: 0006-smart-plugin.patch -Patch6: 0007-nvme-Add-bd_nvme_is_tech_avail-to-the-API-file.patch +Source0: https://github.com/storaged-project/libblockdev/releases/download/%{version}/%{name}-%{version}.tar.gz BuildRequires: make BuildRequires: glib2-devel @@ -641,7 +632,6 @@ A meta-package that pulls all the libblockdev plugins as dependencies. %prep -%autosetup -n %{name}-%{version} -N -b1 %autosetup -n %{name}-%{version} -p1 %build @@ -956,6 +946,11 @@ find %{buildroot} -type f -name "*.la" | xargs %{__rm} %files plugins-all %changelog +* Wed Oct 30 2024 Vojtech Trefny - 3.2.0-1 +- Update to 3.2.0 + Resolves: RHEL-60272 + Resolves: RHEL-50820 + * Tue Oct 29 2024 Troy Dawson - 3.1.0-9 - Bump release for October 2024 mass rebuild: Resolves: RHEL-64018 diff --git a/sources b/sources index 3c4cbda..e7efae0 100644 --- a/sources +++ b/sources @@ -1,2 +1 @@ -SHA512 (libblockdev-3.1.0.tar.gz) = 1e5f8155ceaf1b1f02607d0e2075bfe3b1a2c8d6845de5c90201e6b7aefde3bab7af8e9abf137763e6e9ca42fefe5218697ea63731b3b4952eb004b01cf3719a -SHA512 (smart-tests.tar.gz) = 6ece82eef09c3b22c8f1473653d791bcac7316c3f267368170cea5b152d55603d2a1014c729c4ed190e39660a8734a51989eb53c4a427b843e074f750b1f9a9b +SHA512 (libblockdev-3.2.0.tar.gz) = 02a56f566a768a4f4bc68e3e40c80a080cb5ced58675dfae7986f55aea57f887425ebab19d9c2659887d489f08ace082aca0cf97ed3d95e266fb83a236a2f66c