From 1ed559f41ded842f951c7e34588e73026f615080 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Wed, 20 Nov 2024 13:20:48 +0000 Subject: [PATCH] import RHEL 10 Beta libblockdev-3.1.0-7.el10 --- .gitignore | 3 +- 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 ++++++++++++++++ ...-GError-overwrite-from-libvolume_key.patch | 59 - ...wapon_pagesize-on-systems-with-64k-p.patch | 41 - ...t-when-adding-a-partition-too-big-fo.patch | 32 - ...ction-to-activate-LVs-in-shared-mode.patch | 300 - ...for-starting-and-stopping-VG-locking.patch | 299 - SPECS/libblockdev.spec | 2095 --- libblockdev.spec | 1083 ++ sources | 2 + 15 files changed, 12508 insertions(+), 2827 deletions(-) create mode 100644 0001-misc-RHEL-10-beta-backport.patch create mode 100644 0002-Check-also-for-aliases-in-bd_utils_have_kernel_module.patch create mode 100644 0003-Fix-passing-size-for-pvresize-over-DBus.patch create mode 100644 0004-Upstream-kernel-VDO-support.patch create mode 100644 0005-libext2fs-unused-parameters-fix.patch create mode 100644 0006-smart-plugin.patch delete mode 100644 SOURCES/0001-crypto-Fix-GError-overwrite-from-libvolume_key.patch delete mode 100644 SOURCES/0002-tests-Fix-test_swapon_pagesize-on-systems-with-64k-p.patch delete mode 100644 SOURCES/0003-part-Fix-segfault-when-adding-a-partition-too-big-fo.patch delete mode 100644 SOURCES/0004-lvm-Add-a-function-to-activate-LVs-in-shared-mode.patch delete mode 100644 SOURCES/0005-lvm-Add-support-for-starting-and-stopping-VG-locking.patch delete mode 100644 SPECS/libblockdev.spec create mode 100644 libblockdev.spec create mode 100644 sources diff --git a/.gitignore b/.gitignore index 9768e37..3d3ede5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -SOURCES/libblockdev-2.28.tar.gz +libblockdev-3.1.0.tar.gz +smart-tests.tar.gz diff --git a/0001-misc-RHEL-10-beta-backport.patch b/0001-misc-RHEL-10-beta-backport.patch new file mode 100644 index 0000000..cf16980 --- /dev/null +++ b/0001-misc-RHEL-10-beta-backport.patch @@ -0,0 +1,107 @@ +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 new file mode 100644 index 0000000..d48bd85 --- /dev/null +++ b/0002-Check-also-for-aliases-in-bd_utils_have_kernel_module.patch @@ -0,0 +1,86 @@ +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 new file mode 100644 index 0000000..5d19946 --- /dev/null +++ b/0003-Fix-passing-size-for-pvresize-over-DBus.patch @@ -0,0 +1,65 @@ +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 new file mode 100644 index 0000000..6a01f76 --- /dev/null +++ b/0004-Upstream-kernel-VDO-support.patch @@ -0,0 +1,571 @@ +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 new file mode 100644 index 0000000..cfeb97c --- /dev/null +++ b/0005-libext2fs-unused-parameters-fix.patch @@ -0,0 +1,86 @@ +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 new file mode 100644 index 0000000..6602c9b --- /dev/null +++ b/0006-smart-plugin.patch @@ -0,0 +1,10506 @@ +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/SOURCES/0001-crypto-Fix-GError-overwrite-from-libvolume_key.patch b/SOURCES/0001-crypto-Fix-GError-overwrite-from-libvolume_key.patch deleted file mode 100644 index 4a5ee67..0000000 --- a/SOURCES/0001-crypto-Fix-GError-overwrite-from-libvolume_key.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 7a0e344d0642f76992c943158621d8ee7e5caea3 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Tue, 15 Nov 2022 13:21:25 +0100 -Subject: [PATCH 1/2] crypto: Fix GError overwrite from libvolume_key - ---- - src/plugins/crypto.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c -index 35c38410..9064c8e3 100644 ---- a/src/plugins/crypto.c -+++ b/src/plugins/crypto.c -@@ -2552,13 +2552,14 @@ static gboolean write_escrow_data_file (struct libvk_volume *volume, struct libv - GIOChannel *out_file = NULL; - GIOStatus status = G_IO_STATUS_ERROR; - gsize bytes_written = 0; -+ GError *l_error = NULL; - - packet_data = libvk_volume_create_packet_asymmetric_with_format (volume, &packet_data_size, secret_type, cert, -- ui, LIBVK_PACKET_FORMAT_ASYMMETRIC_WRAP_SECRET_ONLY, error); -- -+ ui, LIBVK_PACKET_FORMAT_ASYMMETRIC_WRAP_SECRET_ONLY, &l_error); - if (!packet_data) { - g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ESCROW_FAILED, -- "Failed to get escrow data"); -+ "Failed to get escrow data: %s", l_error->message); -+ g_clear_error (&l_error); - libvk_volume_free (volume); - return FALSE; - } --- -2.38.1 - - -From 25bf34c4c03e37eb3782dfccf459b9a3f795ddb3 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Wed, 16 Nov 2022 10:26:06 +0100 -Subject: [PATCH 2/2] crypto: Fix double free in write_escrow_data_file - ---- - src/plugins/crypto.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c -index 9064c8e3..2086209e 100644 ---- a/src/plugins/crypto.c -+++ b/src/plugins/crypto.c -@@ -2560,7 +2560,6 @@ static gboolean write_escrow_data_file (struct libvk_volume *volume, struct libv - g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ESCROW_FAILED, - "Failed to get escrow data: %s", l_error->message); - g_clear_error (&l_error); -- libvk_volume_free (volume); - return FALSE; - } - --- -2.38.1 - diff --git a/SOURCES/0002-tests-Fix-test_swapon_pagesize-on-systems-with-64k-p.patch b/SOURCES/0002-tests-Fix-test_swapon_pagesize-on-systems-with-64k-p.patch deleted file mode 100644 index 4712539..0000000 --- a/SOURCES/0002-tests-Fix-test_swapon_pagesize-on-systems-with-64k-p.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 6700dfded31219c99fea054aa10fd68b90bace82 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Mon, 31 Oct 2022 12:43:17 +0100 -Subject: [PATCH] tests: Fix test_swapon_pagesize on systems with 64k pages - ---- - tests/swap_test.py | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/tests/swap_test.py b/tests/swap_test.py -index 0a0f333d..e350f8e8 100644 ---- a/tests/swap_test.py -+++ b/tests/swap_test.py -@@ -1,5 +1,6 @@ - import unittest - import os -+import resource - import overrides_hack - - from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, run_command, run, TestTags, tag_test -@@ -102,8 +103,15 @@ class SwapTestCase(SwapTest): - def test_swapon_pagesize(self): - """Verify that activating swap with different pagesize fails""" - -- # create swap with 64k pagesize -- ret, out, err = run_command("mkswap --pagesize 65536 %s" % self.loop_dev) -+ # pick some wrong page size: 8k on 64k and 64k everywhere else -+ pagesize = resource.getpagesize() -+ if pagesize == 65536: -+ wrong_pagesize = 8192 -+ else: -+ wrong_pagesize = 65536 -+ -+ # create swap with "wrong" pagesize -+ ret, out, err = run_command("mkswap --pagesize %s %s" % (wrong_pagesize, self.loop_dev)) - if ret != 0: - self.fail("Failed to prepare swap for pagesize test: %s %s" % (out, err)) - --- -2.39.2 - diff --git a/SOURCES/0003-part-Fix-segfault-when-adding-a-partition-too-big-fo.patch b/SOURCES/0003-part-Fix-segfault-when-adding-a-partition-too-big-fo.patch deleted file mode 100644 index 84b753d..0000000 --- a/SOURCES/0003-part-Fix-segfault-when-adding-a-partition-too-big-fo.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 9c96e621e9abb0649118d2e1731a09b1fa139579 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Wed, 19 Apr 2023 09:50:38 +0200 -Subject: [PATCH] part: Fix segfault when adding a partition too big for MSDOS - -Resolves: rhbz#2185564 ---- - src/plugins/part.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/src/plugins/part.c b/src/plugins/part.c -index 8b2285f5..28e20c28 100644 ---- a/src/plugins/part.c -+++ b/src/plugins/part.c -@@ -841,6 +841,14 @@ static gboolean resize_part (PedPartition *part, PedDevice *dev, PedDisk *disk, - constr = ped_constraint_any (dev); - - geom = ped_disk_get_max_partition_geometry (disk, part, constr); -+ if (!geom) { -+ set_parted_error (error, BD_PART_ERROR_FAIL); -+ g_prefix_error (error, "Failed to create geometry for partition on device '%s'", dev->path); -+ ped_constraint_destroy (constr); -+ finish_alignment_constraint (disk, orig_flag_state); -+ return FALSE; -+ } -+ - if (!ped_geometry_set_start (geom, start)) { - set_parted_error (error, BD_PART_ERROR_FAIL); - g_prefix_error (error, "Failed to set partition start on device '%s'", dev->path); --- -2.40.1 - diff --git a/SOURCES/0004-lvm-Add-a-function-to-activate-LVs-in-shared-mode.patch b/SOURCES/0004-lvm-Add-a-function-to-activate-LVs-in-shared-mode.patch deleted file mode 100644 index bbf10c7..0000000 --- a/SOURCES/0004-lvm-Add-a-function-to-activate-LVs-in-shared-mode.patch +++ /dev/null @@ -1,300 +0,0 @@ -From ee9c3afec5ef2fe1e9fd50718cd732e267b235ed Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Mon, 24 Apr 2023 11:57:18 +0200 -Subject: [PATCH] lvm: Add a function to activate LVs in shared mode - -Needed by the new blivet feature to support shared LVM setups. ---- - src/lib/plugin_apis/lvm.api | 16 +++++++++ - src/plugins/lvm-dbus.c | 51 ++++++++++++++++++++------- - src/plugins/lvm.c | 53 ++++++++++++++++++++++------- - src/plugins/lvm.h | 1 + - src/python/gi/overrides/BlockDev.py | 5 ++- - tests/lvm_dbus_tests.py | 18 +++++++--- - tests/lvm_test.py | 18 +++++++--- - 7 files changed, 124 insertions(+), 38 deletions(-) - -diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api -index dc8c1348..bac70a75 100644 ---- a/src/lib/plugin_apis/lvm.api -+++ b/src/lib/plugin_apis/lvm.api -@@ -1055,6 +1055,22 @@ gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 si - */ - gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, const BDExtraArg **extra, GError **error); - -+/** -+ * bd_lvm_lvactivate_shared: -+ * @vg_name: name of the VG containing the to-be-activated LV -+ * @lv_name: name of the to-be-activated LV -+ * @ignore_skip: whether to ignore the skip flag or not -+ * @shared: whether to activate the LV in shared mode -+ * @extra: (allow-none) (array zero-terminated=1): extra options for the LV activation -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the @vg_name/@lv_name LV was successfully activated or not -+ * -+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_lvactivate_shared (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error); -+ - /** - * bd_lvm_lvdeactivate: - * @vg_name: name of the VG containing the to-be-deactivated LV -diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c -index b7b4480e..d8e997f9 100644 ---- a/src/plugins/lvm-dbus.c -+++ b/src/plugins/lvm-dbus.c -@@ -2115,6 +2115,27 @@ gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 si - return (*error == NULL); - } - -+static gboolean _lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) { -+ GVariant *params = NULL; -+ GVariantBuilder builder; -+ GVariant *extra_params = NULL; -+ -+ if (shared) -+ params = g_variant_new ("(t)", (guint64) 1 << 6); -+ else -+ params = g_variant_new ("(t)", (guint64) 0); -+ -+ if (ignore_skip) { -+ g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY); -+ g_variant_builder_add (&builder, "{sv}", "-K", g_variant_new ("s", "")); -+ extra_params = g_variant_builder_end (&builder); -+ g_variant_builder_clear (&builder); -+ } -+ call_lv_method_sync (vg_name, lv_name, "Activate", params, extra_params, extra, TRUE, error); -+ -+ return (*error == NULL); -+} -+ - /** - * bd_lvm_lvactivate: - * @vg_name: name of the VG containing the to-be-activated LV -@@ -2129,19 +2150,25 @@ gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 si - * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY - */ - gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, const BDExtraArg **extra, GError **error) { -- GVariant *params = g_variant_new ("(t)", (guint64) 0); -- GVariantBuilder builder; -- GVariant *extra_params = NULL; -- -- if (ignore_skip) { -- g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY); -- g_variant_builder_add (&builder, "{sv}", "-K", g_variant_new ("s", "")); -- extra_params = g_variant_builder_end (&builder); -- g_variant_builder_clear (&builder); -- } -- call_lv_method_sync (vg_name, lv_name, "Activate", params, extra_params, extra, TRUE, error); -+ return _lvm_lvactivate (vg_name, lv_name, ignore_skip, FALSE, extra, error); -+} - -- return (*error == NULL); -+/** -+ * bd_lvm_lvactivate_shared: -+ * @vg_name: name of the VG containing the to-be-activated LV -+ * @lv_name: name of the to-be-activated LV -+ * @ignore_skip: whether to ignore the skip flag or not -+ * @shared: whether to activate the LV in shared mode -+ * @extra: (allow-none) (array zero-terminated=1): extra options for the LV activation -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the @vg_name/@lv_name LV was successfully activated or not -+ * -+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_lvactivate_shared (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) { -+ return _lvm_lvactivate (vg_name, lv_name, ignore_skip, shared, extra, error); - } - - /** -diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c -index 50da656c..d7281339 100644 ---- a/src/plugins/lvm.c -+++ b/src/plugins/lvm.c -@@ -1594,6 +1594,28 @@ gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 si - return success; - } - -+static gboolean _lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) { -+ const gchar *args[5] = {"lvchange", NULL, NULL, NULL, NULL}; -+ guint8 next_arg = 2; -+ gboolean success = FALSE; -+ -+ if (shared) -+ args[1] = "-asy"; -+ else -+ args[1] = "-ay"; -+ -+ if (ignore_skip) { -+ args[next_arg] = "-K"; -+ next_arg++; -+ } -+ args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name); -+ -+ success = call_lvm_and_report_error (args, extra, TRUE, error); -+ g_free ((gchar *) args[next_arg]); -+ -+ return success; -+} -+ - /** - * bd_lvm_lvactivate: - * @vg_name: name of the VG containing the to-be-activated LV -@@ -1608,20 +1630,25 @@ gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 si - * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY - */ - gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, const BDExtraArg **extra, GError **error) { -- const gchar *args[5] = {"lvchange", "-ay", NULL, NULL, NULL}; -- guint8 next_arg = 2; -- gboolean success = FALSE; -- -- if (ignore_skip) { -- args[next_arg] = "-K"; -- next_arg++; -- } -- args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name); -- -- success = call_lvm_and_report_error (args, extra, TRUE, error); -- g_free ((gchar *) args[next_arg]); -+ return _lvm_lvactivate (vg_name, lv_name, ignore_skip, FALSE, extra, error); -+} - -- return success; -+/** -+ * bd_lvm_lvactivate_shared: -+ * @vg_name: name of the VG containing the to-be-activated LV -+ * @lv_name: name of the to-be-activated LV -+ * @ignore_skip: whether to ignore the skip flag or not -+ * @shared: whether to activate the LV in shared mode -+ * @extra: (allow-none) (array zero-terminated=1): extra options for the LV activation -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the @vg_name/@lv_name LV was successfully activated or not -+ * -+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_lvactivate_shared (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) { -+ return _lvm_lvactivate (vg_name, lv_name, ignore_skip, shared, extra, error); - } - - /** -diff --git a/src/plugins/lvm.h b/src/plugins/lvm.h -index 2162d769..244663a4 100644 ---- a/src/plugins/lvm.h -+++ b/src/plugins/lvm.h -@@ -275,6 +275,7 @@ gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean f - gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error); - gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error); - gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, const BDExtraArg **extra, GError **error); -+gboolean bd_lvm_lvactivate_shared (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error); - gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error); - gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error); - gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error); -diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py -index f768c8bd..c7d72f4b 100644 ---- a/src/python/gi/overrides/BlockDev.py -+++ b/src/python/gi/overrides/BlockDev.py -@@ -580,11 +580,10 @@ def lvm_lvresize(vg_name, lv_name, size, extra=None, **kwargs): - return _lvm_lvresize(vg_name, lv_name, size, extra) - __all__.append("lvm_lvresize") - --_lvm_lvactivate = BlockDev.lvm_lvactivate - @override(BlockDev.lvm_lvactivate) --def lvm_lvactivate(vg_name, lv_name, ignore_skip=False, extra=None, **kwargs): -+def lvm_lvactivate(vg_name, lv_name, ignore_skip=False, shared=False, extra=None, **kwargs): - extra = _get_extra(extra, kwargs) -- return _lvm_lvactivate(vg_name, lv_name, ignore_skip, extra) -+ return BlockDev.lvm_lvactivate_shared(vg_name, lv_name, ignore_skip, shared, extra) - __all__.append("lvm_lvactivate") - - _lvm_lvdeactivate = BlockDev.lvm_lvdeactivate -diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py -index 4882da88..67eaccd1 100644 ---- a/tests/lvm_dbus_tests.py -+++ b/tests/lvm_dbus_tests.py -@@ -799,15 +799,15 @@ class LvmTestLVactivateDeactivate(LvmPVVGLVTestCase): - self.assertTrue(succ) - - with self.assertRaises(GLib.GError): -- BlockDev.lvm_lvactivate("nonexistingVG", "testLV", True, None) -+ BlockDev.lvm_lvactivate("nonexistingVG", "testLV", True) - - with self.assertRaises(GLib.GError): -- BlockDev.lvm_lvactivate("testVG", "nonexistingLV", True, None) -+ BlockDev.lvm_lvactivate("testVG", "nonexistingLV", True) - - with self.assertRaises(GLib.GError): -- BlockDev.lvm_lvactivate("nonexistingVG", "nonexistingLV", True, None) -+ BlockDev.lvm_lvactivate("nonexistingVG", "nonexistingLV", True) - -- succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, None) -+ succ = BlockDev.lvm_lvactivate("testVG", "testLV", True) - self.assertTrue(succ) - - with self.assertRaises(GLib.GError): -@@ -822,7 +822,15 @@ class LvmTestLVactivateDeactivate(LvmPVVGLVTestCase): - succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None) - self.assertTrue(succ) - -- succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, None) -+ succ = BlockDev.lvm_lvactivate("testVG", "testLV", True) -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None) -+ self.assertTrue(succ) -+ -+ # try activating in shared mode, unfortunately no way to check whether it really -+ # works or not -+ succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, True) - self.assertTrue(succ) - - succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None) -diff --git a/tests/lvm_test.py b/tests/lvm_test.py -index eb94c917..25e2f109 100644 ---- a/tests/lvm_test.py -+++ b/tests/lvm_test.py -@@ -730,15 +730,15 @@ class LvmTestLVactivateDeactivate(LvmPVVGLVTestCase): - self.assertTrue(succ) - - with self.assertRaises(GLib.GError): -- BlockDev.lvm_lvactivate("nonexistingVG", "testLV", True, None) -+ BlockDev.lvm_lvactivate("nonexistingVG", "testLV", True) - - with self.assertRaises(GLib.GError): -- BlockDev.lvm_lvactivate("testVG", "nonexistingLV", True, None) -+ BlockDev.lvm_lvactivate("testVG", "nonexistingLV", True) - - with self.assertRaises(GLib.GError): -- BlockDev.lvm_lvactivate("nonexistingVG", "nonexistingLV", True, None) -+ BlockDev.lvm_lvactivate("nonexistingVG", "nonexistingLV", True) - -- succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, None) -+ succ = BlockDev.lvm_lvactivate("testVG", "testLV", True) - self.assertTrue(succ) - - with self.assertRaises(GLib.GError): -@@ -753,7 +753,15 @@ class LvmTestLVactivateDeactivate(LvmPVVGLVTestCase): - succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None) - self.assertTrue(succ) - -- succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, None) -+ succ = BlockDev.lvm_lvactivate("testVG", "testLV", True) -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None) -+ self.assertTrue(succ) -+ -+ # try activating in shared mode, unfortunately no way to check whether it really -+ # works or not -+ succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, True) - self.assertTrue(succ) - - succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None) --- -2.41.0 - diff --git a/SOURCES/0005-lvm-Add-support-for-starting-and-stopping-VG-locking.patch b/SOURCES/0005-lvm-Add-support-for-starting-and-stopping-VG-locking.patch deleted file mode 100644 index a2d9eac..0000000 --- a/SOURCES/0005-lvm-Add-support-for-starting-and-stopping-VG-locking.patch +++ /dev/null @@ -1,299 +0,0 @@ -From 5e8429d004445c6f6e6f16cab67cf14cb4d32a65 Mon Sep 17 00:00:00 2001 -From: Vojtech Trefny -Date: Tue, 18 Apr 2023 12:05:35 +0200 -Subject: [PATCH] lvm: Add support for starting and stopping VG locking - ---- - docs/libblockdev-sections.txt | 2 ++ - src/lib/plugin_apis/lvm.api | 27 +++++++++++++++++++ - src/plugins/lvm-dbus.c | 49 ++++++++++++++++++++++++++++++++++- - src/plugins/lvm.c | 41 +++++++++++++++++++++++++++++ - src/plugins/lvm.h | 3 +++ - tests/lvm_dbus_tests.py | 33 +++++++++++++++++++++++ - tests/lvm_test.py | 32 +++++++++++++++++++++++ - 7 files changed, 186 insertions(+), 1 deletion(-) - -diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt -index 512820c2..7377dd17 100644 ---- a/docs/libblockdev-sections.txt -+++ b/docs/libblockdev-sections.txt -@@ -286,6 +286,8 @@ bd_lvm_vgactivate - bd_lvm_vgdeactivate - bd_lvm_vgextend - bd_lvm_vgreduce -+bd_lvm_vglock_start -+bd_lvm_vglock_stop - bd_lvm_vginfo - bd_lvm_vgs - bd_lvm_lvorigin -diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api -index 54c47a93..14f2620a 100644 ---- a/src/lib/plugin_apis/lvm.api -+++ b/src/lib/plugin_apis/lvm.api -@@ -601,6 +601,7 @@ typedef enum { - BD_LVM_TECH_CACHE_CALCS, - BD_LVM_TECH_GLOB_CONF, - BD_LVM_TECH_VDO, -+ BD_LVM_TECH_SHARED, - } BDLVMTech; - - typedef enum { -@@ -941,6 +942,32 @@ gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExt - */ - gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error); - -+/** -+ * bd_lvm_vglock_start: -+ * @vg_name: a shared VG to start the lockspace in lvmlockd -+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the lock was successfully started for @vg_name or not -+ * -+ * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error); -+ -+/** -+ * bd_lvm_vglock_stop: -+ * @vg_name: a shared VG to stop the lockspace in lvmlockd -+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the lock was successfully stopped for @vg_name or not -+ * -+ * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error); -+ - /** - * bd_lvm_vginfo: - * @vg_name: a VG to get information about -diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c -index e1f946fe..ad44dfb3 100644 ---- a/src/plugins/lvm-dbus.c -+++ b/src/plugins/lvm-dbus.c -@@ -1848,10 +1848,57 @@ gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExt - return ((*error) == NULL); - } - -+gboolean _vglock_start_stop (const gchar *vg_name, gboolean start, const BDExtraArg **extra, GError **error) { -+ GVariantBuilder builder; -+ GVariant *params = NULL; -+ -+ g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY); -+ if (start) -+ g_variant_builder_add (&builder, "{sv}", "--lockstart", g_variant_new ("s", "")); -+ else -+ g_variant_builder_add (&builder, "{sv}", "--lockstop", g_variant_new ("s", "")); -+ params = g_variant_builder_end (&builder); -+ g_variant_builder_clear (&builder); -+ -+ call_lvm_obj_method_sync (vg_name, VG_INTF, "Change", NULL, params, extra, TRUE, error); -+ -+ return ((*error) == NULL); -+} -+ -+/** -+ * bd_lvm_vglock_start: -+ * @vg_name: a shared VG to start the lockspace in lvmlockd -+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the lock was successfully started for @vg_name or not -+ * -+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error) { -+ return _vglock_start_stop (vg_name, TRUE, extra, error); -+} -+ -+/** -+ * bd_lvm_vglock_stop: -+ * @vg_name: a shared VG to stop the lockspace in lvmlockd -+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the lock was successfully stopped for @vg_name or not -+ * -+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error) { -+ return _vglock_start_stop (vg_name, FALSE, extra, error); -+} -+ - /** - * bd_lvm_vginfo: - * @vg_name: a VG to get information about -- * @error: (out): place to store error (if any) -+ * @error: (out) (optional): place to store error (if any) - * - * Returns: (transfer full): information about the @vg_name VG or %NULL in case - * of error (the @error) gets populated in those cases) -diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c -index 8bb3ae24..1aaf6747 100644 ---- a/src/plugins/lvm.c -+++ b/src/plugins/lvm.c -@@ -1316,6 +1316,47 @@ gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExt - return call_lvm_and_report_error (args, extra, TRUE, error); - } - -+gboolean _vglock_start_stop (const gchar *vg_name, gboolean start, const BDExtraArg **extra, GError **error) { -+ const gchar *args[4] = {"vgchange", NULL, vg_name, NULL}; -+ -+ if (start) -+ args[1] = "--lockstart"; -+ else -+ args[1] = "--lockstop"; -+ -+ return call_lvm_and_report_error (args, extra, TRUE, error); -+} -+ -+/** -+ * bd_lvm_vglock_start: -+ * @vg_name: a shared VG to start the lockspace in lvmlockd -+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the lock was successfully started for @vg_name or not -+ * -+ * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error) { -+ return _vglock_start_stop (vg_name, TRUE, extra, error); -+} -+ -+/** -+ * bd_lvm_vglock_stop: -+ * @vg_name: a shared VG to stop the lockspace in lvmlockd -+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command -+ * (just passed to LVM as is) -+ * @error: (out): place to store error (if any) -+ * -+ * Returns: whether the lock was successfully stopped for @vg_name or not -+ * -+ * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY -+ */ -+gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error) { -+ return _vglock_start_stop (vg_name, FALSE, extra, error); -+} -+ - /** - * bd_lvm_vginfo: - * @vg_name: a VG to get information about -diff --git a/src/plugins/lvm.h b/src/plugins/lvm.h -index 244663a4..da14cc1a 100644 ---- a/src/plugins/lvm.h -+++ b/src/plugins/lvm.h -@@ -216,6 +216,7 @@ typedef enum { - BD_LVM_TECH_CACHE_CALCS, - BD_LVM_TECH_GLOB_CONF, - BD_LVM_TECH_VDO, -+ BD_LVM_TECH_SHARED, - } BDLVMTech; - - typedef enum { -@@ -266,6 +267,8 @@ gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GErr - gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error); - gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error); - gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error); -+gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error); -+gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error); - BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error); - BDLVMVGdata** bd_lvm_vgs (GError **error); - -diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py -index 862a44cf..2a92c7c1 100644 ---- a/tests/lvm_dbus_tests.py -+++ b/tests/lvm_dbus_tests.py -@@ -612,6 +612,39 @@ class LvmTestVGs(LvmPVVGTestCase): - succ = BlockDev.lvm_pvremove(self.loop_dev, None) - self.assertTrue(succ) - -+@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running") -+class LvmTestVGLocking(LvmPVVGTestCase): -+ @tag_test(TestTags.UNSAFE) -+ def test_vglock_stop_start(self): -+ """Verify that it is possible to start and stop locking on a VG""" -+ -+ # better not do anything if lvmlockd is running, shared VGs have -+ # a tendency to wreak havoc on your system if you look at them wrong -+ ret, _out, _err = run_command("systemctl is-active lvmlockd") -+ if ret == 0: -+ self.skipTest("lvmlockd is running, skipping") -+ -+ _ret, out, _err = run_command("lvm config 'global/use_lvmlockd'") -+ if "use_lvmlockd=0" not in out: -+ self.skipTest("lvmlockd is enabled, skipping") -+ -+ succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None) -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None) -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None) -+ self.assertTrue(succ) -+ -+ # this actually doesn't "test" anything, the commands will just say lvmlockd is not -+ # running and return 0, but that's good enough for us -+ succ = BlockDev.lvm_vglock_start("testVG") -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_vglock_stop("testVG") -+ self.assertTrue(succ) -+ - @unittest.skipUnless(lvm_dbus_running, "LVM DBus not running") - class LvmPVVGLVTestCase(LvmPVVGTestCase): - def _clean_up(self): -diff --git a/tests/lvm_test.py b/tests/lvm_test.py -index 96f1670d..3ab11f04 100644 ---- a/tests/lvm_test.py -+++ b/tests/lvm_test.py -@@ -585,6 +585,38 @@ class LvmTestVGs(LvmPVVGTestCase): - succ = BlockDev.lvm_pvremove(self.loop_dev, None) - self.assertTrue(succ) - -+class LvmTestVGLocking(LvmPVVGTestCase): -+ @tag_test(TestTags.UNSAFE) -+ def test_vglock_stop_start(self): -+ """Verify that it is possible to start and stop locking on a VG""" -+ -+ # better not do anything if lvmlockd is running, shared VGs have -+ # a tendency to wreak havoc on your system if you look at them wrong -+ ret, _out, _err = run_command("systemctl is-active lvmlockd") -+ if ret == 0: -+ self.skipTest("lvmlockd is running, skipping") -+ -+ _ret, out, _err = run_command("lvm config 'global/use_lvmlockd'") -+ if "use_lvmlockd=0" not in out: -+ self.skipTest("lvmlockd is enabled, skipping") -+ -+ succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None) -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None) -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None) -+ self.assertTrue(succ) -+ -+ # this actually doesn't "test" anything, the commands will just say lvmlockd is not -+ # running and return 0, but that's good enough for us -+ succ = BlockDev.lvm_vglock_start("testVG") -+ self.assertTrue(succ) -+ -+ succ = BlockDev.lvm_vglock_stop("testVG") -+ self.assertTrue(succ) -+ - class LvmPVVGLVTestCase(LvmPVVGTestCase): - def _clean_up(self): - try: --- -2.41.0 - diff --git a/SPECS/libblockdev.spec b/SPECS/libblockdev.spec deleted file mode 100644 index 8143b0e..0000000 --- a/SPECS/libblockdev.spec +++ /dev/null @@ -1,2095 +0,0 @@ -%define with_python2 1 -%define with_python3 1 -%define with_gtk_doc 1 -%define with_bcache 1 -%define with_btrfs 1 -%define with_crypto 1 -%define with_dm 1 -%define with_loop 1 -%define with_lvm 1 -%define with_lvm_dbus 1 -%define with_mdraid 1 -%define with_mpath 1 -%define with_swap 1 -%define with_kbd 1 -%define with_part 1 -%define with_fs 1 -%define with_nvdimm 1 -%define with_vdo 1 -%define with_gi 1 -%define with_escrow 1 -%define with_dmraid 0 -%define with_tools 1 - -# python2 is not available on RHEL > 7 and not needed on Fedora > 29 -%if 0%{?rhel} > 7 || 0%{?fedora} > 29 || %{with_python2} == 0 -%define with_python2 0 -%define python2_copts --without-python2 -%endif - -# python3 is not available on older RHEL -%if (! 0%{?fedora} && 0%{?rhel} <= 7) || %{with_python3} == 0 -%define with_python3 0 -%define python3_copts --without-python3 -%endif - -# bcache is not available on RHEL -%if (0%{?rhel}) || %{with_bcache} == 0 -%define with_bcache 0 -%define bcache_copts --without-bcache -%endif - -# lvm_dbus is not available on older RHEL -%if (! 0%{?fedora} && 0%{?rhel} <= 7) || %{with_lvm_dbus} == 0 -%define with_lvm_dbus 0 -%define lvm_dbus_copts --without-lvm-dbus -%endif - -# vdo is not available on non-x86_64 on older RHEL -%if (0%{?rhel} && 0%{?rhel} <= 7) -%ifnarch x86_64 aarch64 s390x ppc64le -%define with_vdo 0 -%define vdo_copts --without-vdo -%endif -%endif - -# btrfs is not available on RHEL > 7 -%if 0%{?rhel} > 7 || %{with_btrfs} == 0 -%define with_btrfs 0 -%define btrfs_copts --without-btrfs -%endif - -# dmraid is not available on RHEL > 7 -%if 0%{?rhel} > 7 -%define with_dmraid 0 -%endif - -%if %{with_btrfs} != 1 -%define btrfs_copts --without-btrfs -%endif -%if %{with_crypto} != 1 -%define crypto_copts --without-crypto -%else -%if %{with_escrow} != 1 -%define crypto_copts --without-escrow -%endif -%endif -%if %{with_dm} != 1 -%define dm_copts --without-dm -%else -%if %{with_dmraid} != 1 -%define dm_copts --without-dmraid -%endif -%endif -%if %{with_loop} != 1 -%define loop_copts --without-loop -%endif -%if %{with_lvm} != 1 -%define lvm_copts --without-lvm -%endif -%if %{with_lvm_dbus} != 1 -%define lvm_dbus_copts --without-lvm_dbus -%endif -%if %{with_mdraid} != 1 -%define mdraid_copts --without-mdraid -%endif -%if %{with_mpath} != 1 -%define mpath_copts --without-mpath -%endif -%if %{with_swap} != 1 -%define swap_copts --without-swap -%endif -%if %{with_kbd} != 1 -%define kbd_copts --without-kbd -%endif -%if %{with_part} != 1 -%define part_copts --without-part -%endif -%if %{with_fs} != 1 -%define fs_copts --without-fs -%endif -%if %{with_nvdimm} != 1 -%define nvdimm_copts --without-nvdimm -%endif -%if %{with_vdo} != 1 -%define vdo_copts --without-vdo -%endif -%if %{with_tools} != 1 -%define tools_copts --without-tools -%endif -%if %{with_gi} != 1 -%define gi_copts --disable-introspection -%endif - -%define configure_opts %{?python2_copts} %{?python3_copts} %{?bcache_copts} %{?lvm_dbus_copts} %{?btrfs_copts} %{?crypto_copts} %{?dm_copts} %{?loop_copts} %{?lvm_copts} %{?lvm_dbus_copts} %{?mdraid_copts} %{?mpath_copts} %{?swap_copts} %{?kbd_copts} %{?part_copts} %{?fs_copts} %{?nvdimm_copts} %{?vdo_copts} %{?tools_copts} %{?gi_copts} - -Name: libblockdev -Version: 2.28 -Release: 6%{?dist} -Summary: A library for low-level manipulation with block devices -License: LGPLv2+ -URL: https://github.com/storaged-project/libblockdev -Source0: https://github.com/storaged-project/libblockdev/releases/download/%{version}-%{release}/%{name}-%{version}.tar.gz -Patch0: 0001-crypto-Fix-GError-overwrite-from-libvolume_key.patch -Patch1: 0002-tests-Fix-test_swapon_pagesize-on-systems-with-64k-p.patch -Patch2: 0003-part-Fix-segfault-when-adding-a-partition-too-big-fo.patch -Patch3: 0004-lvm-Add-a-function-to-activate-LVs-in-shared-mode.patch -Patch4: 0005-lvm-Add-support-for-starting-and-stopping-VG-locking.patch - -BuildRequires: make -BuildRequires: glib2-devel -%if %{with_gi} -BuildRequires: gobject-introspection-devel -%endif -%if %{with_python2} -BuildRequires: python2-devel -%else -# Obsolete the python2 subpackage to avoid errors on upgrade -Obsoletes: python2-blockdev < %{version}-%{release} -%endif -%if %{with_python3} -BuildRequires: python3-devel -%endif -%if %{with_gtk_doc} -BuildRequires: gtk-doc -%endif -BuildRequires: glib2-doc -BuildRequires: autoconf-archive - -Requires: %{name}-utils%{?_isa} = %{version}-%{release} - -# Needed for the escrow tests in tests/crypto_test.py, but not used to build -# BuildRequires: volume_key -# BuildRequires: nss-tools - -# Needed for python 2 vs. 3 compatibility in the tests, but not used to build -# BuildRequires: python2-six -# BuildRequires: python3-six - -%description -The libblockdev is a C library with GObject introspection support that can be -used for doing low-level operations with block devices like setting up LVM, -BTRFS, LUKS or MD RAID. The library uses plugins (LVM, BTRFS,...) and serves as -a thin wrapper around its plugins' functionality. All the plugins, however, can -be used as standalone libraries. One of the core principles of libblockdev is -that it is stateless from the storage configuration's perspective (e.g. it has -no information about VGs when creating an LV). - -%package devel -Summary: Development files for libblockdev -Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: glib2-devel -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} - -%description devel -This package contains header files and pkg-config files needed for development -with the libblockdev library. - -%if %{with_python2} -%package -n python2-blockdev -Summary: Python2 gobject-introspection bindings for libblockdev -Requires: %{name}%{?_isa} = %{version}-%{release} - -%if 0%{?fedora} <= 26 || 0%{?rhel} <= 7 -Requires: pygobject3-base -%else -Requires: python2-gobject-base -%endif -%{?python_provide:%python_provide python2-blockdev} - -%description -n python2-blockdev -This package contains enhancements to the gobject-introspection bindings for -libblockdev in Python2. -%endif - -%if %{with_python3} -%package -n python3-blockdev -Summary: Python3 gobject-introspection bindings for libblockdev -Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: python3-gobject-base -%{?python_provide:%python_provide python3-blockdev} - -%description -n python3-blockdev -This package contains enhancements to the gobject-introspection bindings for -libblockdev in Python3. -%endif - -%package utils -BuildRequires: kmod-devel -Summary: A library with utility functions for the libblockdev library - -%description utils -The libblockdev-utils is a library providing utility functions used by the -libblockdev library and its plugins. - -%package utils-devel -Summary: Development files for libblockdev-utils -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description utils-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-utils library. - - -%if %{with_btrfs} -%package btrfs -BuildRequires: libbytesize-devel -Summary: The BTRFS plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: btrfs-progs - -%description btrfs -The libblockdev library plugin (and in the same time a standalone library) -providing the BTRFS-related functionality. - -%package btrfs-devel -Summary: Development files for the libblockdev-btrfs plugin/library -Requires: %{name}-btrfs%{?_isa} = %{version}-%{release} -Requires: glib2-devel -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} - -%description btrfs-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-btrfs plugin/library. -%endif - - -%if %{with_crypto} -%package crypto -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -BuildRequires: cryptsetup-devel -BuildRequires: libblkid-devel - -%if %{with_escrow} -BuildRequires: volume_key-devel >= 0.3.9-7 -BuildRequires: nss-devel -%endif - -Summary: The crypto plugin for the libblockdev library - -%description crypto -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to encrypted devices (LUKS). - -%package crypto-devel -Summary: Development files for the libblockdev-crypto plugin/library -Requires: %{name}-crypto%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description crypto-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-crypto plugin/library. -%endif - - -%if %{with_dm} -%package dm -BuildRequires: device-mapper-devel -%if %{with_dmraid} -BuildRequires: dmraid-devel -%endif -BuildRequires: systemd-devel -Summary: The Device Mapper plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: device-mapper -%if %{with_dmraid} -Requires: dmraid -%endif - -%description dm -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to Device Mapper. - -%package dm-devel -Summary: Development files for the libblockdev-dm plugin/library -Requires: %{name}-dm%{?_isa} = %{version}-%{release} -Requires: glib2-devel -Requires: device-mapper-devel -Requires: systemd-devel -%if %{with_dmraid} -Requires: dmraid-devel -%endif -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} - -%description dm-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-dm plugin/library. -%endif - - -%if %{with_fs} -%package fs -BuildRequires: parted-devel -BuildRequires: libblkid-devel -BuildRequires: libmount-devel -Summary: The FS plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} - -%description fs -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to operations with file systems. - -%package fs-devel -Summary: Development files for the libblockdev-fs plugin/library -Requires: %{name}-fs%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel -Requires: xfsprogs -Requires: dosfstools - -%description fs-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-fs plugin/library. -%endif - - -%if %{with_kbd} -%package kbd -BuildRequires: libbytesize-devel -Summary: The KBD plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -%if %{with_bcache} -Requires: bcache-tools >= 1.0.8 -%endif - -%description kbd -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to kernel block devices (namely zRAM and -Bcache). - -%package kbd-devel -Summary: Development files for the libblockdev-kbd plugin/library -Requires: %{name}-kbd%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description kbd-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-kbd plugin/library. -%endif - - -%if %{with_loop} -%package loop -Summary: The loop plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} - -%description loop -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to loop devices. - -%package loop-devel -Summary: Development files for the libblockdev-loop plugin/library -Requires: %{name}-loop%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description loop-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-loop plugin/library. -%endif - - -%if %{with_lvm} -%package lvm -BuildRequires: device-mapper-devel -Summary: The LVM plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: lvm2 - -%description lvm -The libblockdev library plugin (and in the same time a standalone library) -providing the LVM-related functionality. - -%package lvm-devel -Summary: Development files for the libblockdev-lvm plugin/library -Requires: %{name}-lvm%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description lvm-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-lvm plugin/library. -%endif - -%if %{with_lvm_dbus} -%package lvm-dbus -BuildRequires: device-mapper-devel -Summary: The LVM plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: lvm2-dbusd >= 2.02.156 - -%description lvm-dbus -The libblockdev library plugin (and in the same time a standalone library) -providing the LVM-related functionality utilizing the LVM DBus API. - -%package lvm-dbus-devel -Summary: Development files for the libblockdev-lvm-dbus plugin/library -Requires: %{name}-lvm-dbus%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description lvm-dbus-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-lvm-dbus plugin/library. -%endif - - -%if %{with_mdraid} -%package mdraid -BuildRequires: libbytesize-devel -Summary: The MD RAID plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: mdadm - -%description mdraid -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to MD RAID. - -%package mdraid-devel -Summary: Development files for the libblockdev-mdraid plugin/library -Requires: %{name}-mdraid%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description mdraid-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-mdraid plugin/library. -%endif - - -%if %{with_mpath} -%package mpath -BuildRequires: device-mapper-devel -Summary: The multipath plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: device-mapper-multipath - -%description mpath -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to multipath devices. - -%package mpath-devel -Summary: Development files for the libblockdev-mpath plugin/library -Requires: %{name}-mpath%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description mpath-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-mpath plugin/library. -%endif - -%if %{with_nvdimm} -%package nvdimm -BuildRequires: ndctl-devel -BuildRequires: libuuid-devel -Summary: The NVDIMM plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: ndctl - -%description nvdimm -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to operations with NVDIMM devices. - -%package nvdimm-devel -Summary: Development files for the libblockdev-nvdimm plugin/library -Requires: %{name}-nvdimm%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description nvdimm-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-nvdimm plugin/library. -%endif - - -%if %{with_part} -%package part -BuildRequires: parted-devel -Summary: The partitioning plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: gdisk -Requires: util-linux - -%description part -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to partitioning. - -%package part-devel -Summary: Development files for the libblockdev-part plugin/library -Requires: %{name}-part%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description part-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-part plugin/library. -%endif - - -%if %{with_swap} -%package swap -BuildRequires: libblkid-devel -Summary: The swap plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} -Requires: util-linux - -%description swap -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to swap devices. - -%package swap-devel -Summary: Development files for the libblockdev-swap plugin/library -Requires: %{name}-swap%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description swap-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-swap plugin/library. -%endif - - -%if %{with_vdo} -%package vdo -BuildRequires: libbytesize-devel -BuildRequires: libyaml-devel -Summary: The vdo plugin for the libblockdev library -Requires: %{name}-utils%{?_isa} = %{version}-%{release} - -# weak dependencies doesn't work on older RHEL -%if (0%{?rhel} && 0%{?rhel} <= 7) -Requires: vdo -Requires: kmod-kvdo -%else -# we want to build the plugin everywhere but the dependencies might not be -# available so just use weak dependency -Recommends: vdo -Recommends: kmod-kvdo -%endif - -%description vdo -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to VDO devices. - -%package vdo-devel -Summary: Development files for the libblockdev-vdo plugin/library -Requires: %{name}-vdo%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description vdo-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-vdo plugin/library. -%endif - -%if %{with_tools} -%package tools -Summary: Various nice tools based on libblockdev -Requires: %{name} = %{version}-%{release} -Requires: %{name}-lvm = %{version}-%{release} -BuildRequires: libbytesize-devel -%if %{with_lvm_dbus} == 1 -Recommends: %{name}-lvm-dbus -%endif - -%description tools -Various nice storage-related tools based on libblockdev. - -%endif - -%ifarch s390 s390x -%package s390 -Summary: The s390 plugin for the libblockdev library -Requires: s390utils -Requires: %{name}-utils%{?_isa} = %{version}-%{release} - -%description s390 -The libblockdev library plugin (and in the same time a standalone library) -providing the functionality related to s390 devices. - -%package s390-devel -Summary: Development files for the libblockdev-s390 plugin/library -Requires: %{name}-s390%{?_isa} = %{version}-%{release} -Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} -Requires: glib2-devel - -%description s390-devel -This package contains header files and pkg-config files needed for development -with the libblockdev-s390 plugin/library. -%endif - -%package plugins-all -Summary: Meta-package that pulls all the libblockdev plugins as dependencies -Requires: %{name}%{?_isa} = %{version}-%{release} - -%if %{with_btrfs} -Requires: %{name}-btrfs%{?_isa} = %{version}-%{release} -%endif - -%if %{with_crypto} -Requires: %{name}-crypto%{?_isa} = %{version}-%{release} -%endif - -%if %{with_dm} -Requires: %{name}-dm%{?_isa} = %{version}-%{release} -%endif - -%if %{with_fs} -Requires: %{name}-fs%{?_isa} = %{version}-%{release} -%endif - -%if %{with_kbd} -Requires: %{name}-kbd%{?_isa} = %{version}-%{release} -%endif - -%if %{with_loop} -Requires: %{name}-loop%{?_isa} = %{version}-%{release} -%endif - -%if %{with_lvm} -Requires: %{name}-lvm%{?_isa} = %{version}-%{release} -%endif - -%if %{with_mdraid} -Requires: %{name}-mdraid%{?_isa} = %{version}-%{release} -%endif - -%if %{with_mpath} -Requires: %{name}-mpath%{?_isa} = %{version}-%{release} -%endif - -%if %{with_nvdimm} -Requires: %{name}-nvdimm%{?_isa} = %{version}-%{release} -%endif - -%if %{with_part} -Requires: %{name}-part%{?_isa} = %{version}-%{release} -%endif - -%if %{with_swap} -Requires: %{name}-swap%{?_isa} = %{version}-%{release} -%endif - -%if %{with_vdo} -Requires: %{name}-vdo%{?_isa} = %{version}-%{release} -%endif - -%ifarch s390 s390x -Requires: %{name}-s390%{?_isa} = %{version}-%{release} -%endif - -%description plugins-all -A meta-package that pulls all the libblockdev plugins as dependencies. - - -%prep -%autosetup -n %{name}-%{version} -p1 - -%build -autoreconf -ivf -%configure %{?configure_opts} -%{__make} %{?_smp_mflags} - -%install -%{make_install} -find %{buildroot} -type f -name "*.la" | xargs %{__rm} - - -%ldconfig_scriptlets -%ldconfig_scriptlets utils - -%if %{with_btrfs} -%ldconfig_scriptlets btrfs -%endif - -%if %{with_crypto} -%ldconfig_scriptlets crypto -%endif - -%if %{with_dm} -%ldconfig_scriptlets dm -%endif - -%if %{with_fs} -%ldconfig_scriptlets fs -%endif - -%if %{with_loop} -%ldconfig_scriptlets loop -%endif - -%if %{with_lvm} -%ldconfig_scriptlets lvm -%endif - -%if %{with_lvm_dbus} -%ldconfig_scriptlets lvm-dbus -%endif - -%if %{with_mdraid} -%ldconfig_scriptlets mdraid -%endif - -%if %{with_mpath} -%ldconfig_scriptlets mpath -%endif - -%if %{with_nvdimm} -%ldconfig_scriptlets nvdimm -%endif - -%if %{with_part} -%ldconfig_scriptlets part -%endif - -%if %{with_swap} -%ldconfig_scriptlets swap -%endif - -%if %{with_vdo} -%ldconfig_scriptlets vdo -%endif - -%ifarch s390 s390x -%ldconfig_scriptlets s390 -%endif - -%if %{with_kbd} -%ldconfig_scriptlets kbd -%endif - - -%files -%{!?_licensedir:%global license %%doc} -%license LICENSE -%{_libdir}/libblockdev.so.* -%if %{with_gi} -%{_libdir}/girepository*/BlockDev*.typelib -%endif -%dir %{_sysconfdir}/libblockdev -%dir %{_sysconfdir}/libblockdev/conf.d -%config %{_sysconfdir}/libblockdev/conf.d/00-default.cfg - -%files devel -%doc features.rst specs.rst -%{_libdir}/libblockdev.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/blockdev.h -%{_includedir}/blockdev/plugins.h -%{_libdir}/pkgconfig/blockdev.pc -%if %{with_gtk_doc} -%{_datadir}/gtk-doc/html/libblockdev -%endif -%if %{with_gi} -%{_datadir}/gir*/BlockDev*.gir -%endif - -%if %{with_python2} -%files -n python2-blockdev -%{python2_sitearch}/gi/overrides/* -%endif - -%if %{with_python3} -%files -n python3-blockdev -%{python3_sitearch}/gi/overrides/BlockDev* -%{python3_sitearch}/gi/overrides/__pycache__/BlockDev* -%endif - -%files utils -%{_libdir}/libbd_utils.so.* -%{_libdir}/libbd_part_err.so.* - -%files utils-devel -%{_libdir}/libbd_utils.so -%{_libdir}/libbd_part_err.so -%{_libdir}/pkgconfig/blockdev-utils.pc -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/utils.h -%{_includedir}/blockdev/sizes.h -%{_includedir}/blockdev/exec.h -%{_includedir}/blockdev/extra_arg.h -%{_includedir}/blockdev/dev_utils.h -%{_includedir}/blockdev/module.h -%{_includedir}/blockdev/dbus.h - - -%if %{with_btrfs} -%files btrfs -%{_libdir}/libbd_btrfs.so.* - -%files btrfs-devel -%{_libdir}/libbd_btrfs.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/btrfs.h -%endif - - -%if %{with_crypto} -%files crypto -%{_libdir}/libbd_crypto.so.* - -%files crypto-devel -%{_libdir}/libbd_crypto.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/crypto.h -%endif - - -%if %{with_dm} -%files dm -%{_libdir}/libbd_dm.so.* - -%files dm-devel -%{_libdir}/libbd_dm.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/dm.h -%endif - - -%if %{with_fs} -%files fs -%{_libdir}/libbd_fs.so.* - -%files fs-devel -%{_libdir}/libbd_fs.so -%dir %{_includedir}/blockdev -%dir %{_includedir}/blockdev/fs -%{_includedir}/blockdev/fs.h -%{_includedir}/blockdev/fs/*.h -%endif - - -%if %{with_kbd} -%files kbd -%{_libdir}/libbd_kbd.so.* - -%files kbd-devel -%{_libdir}/libbd_kbd.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/kbd.h -%endif - - -%if %{with_loop} -%files loop -%{_libdir}/libbd_loop.so.* - -%files loop-devel -%{_libdir}/libbd_loop.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/loop.h -%endif - - -%if %{with_lvm} -%files lvm -%{_libdir}/libbd_lvm.so.* - -%files lvm-devel -%{_libdir}/libbd_lvm.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/lvm.h -%endif - - -%if %{with_lvm_dbus} -%files lvm-dbus -%{_libdir}/libbd_lvm-dbus.so.* -%config %{_sysconfdir}/libblockdev/conf.d/10-lvm-dbus.cfg - -%files lvm-dbus-devel -%{_libdir}/libbd_lvm-dbus.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/lvm.h -%endif - - -%if %{with_mdraid} -%files mdraid -%{_libdir}/libbd_mdraid.so.* - -%files mdraid-devel -%{_libdir}/libbd_mdraid.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/mdraid.h -%endif - - -%if %{with_mpath} -%files mpath -%{_libdir}/libbd_mpath.so.* - -%files mpath-devel -%{_libdir}/libbd_mpath.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/mpath.h -%endif - - -%if %{with_nvdimm} -%files nvdimm -%{_libdir}/libbd_nvdimm.so.* - -%files nvdimm-devel -%{_libdir}/libbd_nvdimm.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/nvdimm.h -%endif - - -%if %{with_part} -%files part -%{_libdir}/libbd_part.so.* - -%files part-devel -%{_libdir}/libbd_part.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/part.h -%endif - - -%if %{with_swap} -%files swap -%{_libdir}/libbd_swap.so.* - -%files swap-devel -%{_libdir}/libbd_swap.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/swap.h -%endif - - -%if %{with_vdo} -%files vdo -%{_libdir}/libbd_vdo.so.* - -%files vdo-devel -%{_libdir}/libbd_vdo.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/vdo.h -%endif - -%if %{with_tools} -%files tools -%{_bindir}/lvm-cache-stats -%endif - -%ifarch s390 s390x -%files s390 -%{_libdir}/libbd_s390.so.* - -%files s390-devel -%{_libdir}/libbd_s390.so -%dir %{_includedir}/blockdev -%{_includedir}/blockdev/s390.h -%endif - -%files plugins-all - -%changelog -* Wed Nov 08 2023 Vojtech Trefny - 2.28-10 -- lvm: Add support for starting and stopping VG locking - Resolves: RHEL-15923 - -* Thu Oct 19 2023 Vojtech Trefny - 2.28-5 -- lvm: Add a function to activate LVs in shared mode - Resolves: RHEL-14023 - -* Tue May 16 2023 Vojtech Trefny - 2.28-4 -- Fix segfault when adding a partition too big for MSDOS - Resolves: rhbz#2207500 - -* Mon Apr 03 2023 Vojtech Trefny - 2.28-3 -- Fix test_swapon_pagesize on systems with 64k pages - Resolves: rhbz#2138698 - -* Wed Nov 30 2022 Vojtech Trefny - 2.28-2 -- Fix double free in write_escrow_data_file - Resolves: rhbz#2142660 - -* Wed Sep 14 2022 Vojtech Trefny - 2.28-1 -- Rebase to the latest upstream release 2.28 - Resolves: rhbz#2123347 - -* Mon Aug 08 2022 Vojtech Trefny - 2.24-11 -- mdraid: Fix use after free - Related: rhbz#2078815 - -* Mon Aug 08 2022 Vojtech Trefny - 2.24-10 -- mdraid: Fix copy-paste error when checking return value - Related: rhbz#2078815 - -* Fri Aug 05 2022 Vojtech Trefny - 2.24-9 -- Fix getting UUID for DDF containers - Resolves: rhbz#2078815 - -* Tue Dec 07 2021 Vojtech Trefny - 2.24-8 -- Fix vdo stats calculation - Resolves: rhbz#2023883 - -* Wed Jun 30 2021 Vojtech Trefny - 2.24-7 -- Add workarounds for some LVM test issues - Resolves: rhbz#1974352 - -* Fri May 14 2021 Vojtech Trefny - 2.24-6 -- Fix default key size for non XTS ciphers - Resolves: rhbz#1931847 - -* Mon Jan 11 2021 Vojtech Trefny - 2.24-5 -- Fix LVM thin metadata calculation fix - Resolves: rhbz#1901714 - -* Mon Dec 14 2020 Vojtech Trefny - 2.24-4 -- LVM thin metadata calculation fix - Resolves: rhbz#1901714 - -* Wed Nov 18 2020 Vojtech Trefny - 2.24-3 -- exec: Polling fixes - Resolves: rhbz#1884689 - -* Mon Nov 09 2020 Vojtech Trefny - 2.24-2 -- exec: Fix setting locale for util calls - Resolves: rhbz#1880031 - -* Fri May 22 2020 Vojtech Trefny - 2.24-1 -- Rebased to the latest upstream release 2.24 - Resolves: rhbz#1824153 - -* Mon Dec 02 2019 Vojtech Trefny - 2.19-12 -- Use cryptsetup to check LUKS2 label - Resolves: rhbz#1778689 -- Fix expected cache pool name with newest LVM - Related: rhbz#1778689 - -* Thu Jun 06 2019 Vojtech Trefny - 2.19-11 -- Fix checking swap status on lvm/md (vtrefny) - Resolves: rhbz#1649815 - -* Thu May 30 2019 Tomas Bzatek - 2.19-10 -- Memory leak fixes (tbzatek) - Resolves: rhbz#1714276 - -* Mon May 06 2019 Vojtech Trefny - 2.19-9 -- Remove device-mapper-multipath dependency from fs and part plugins (vtrefny) - Resolves: rhbz#1700297 - -* Mon Apr 08 2019 Vojtech Trefny - 2.19-8 -- Allow running tests against installed version of libblockdev (vtrefny) - Related: rhbz#1679668 - -* Mon Jan 07 2019 Vojtech Trefny - 2.19-7 -- Use major/minor macros from sys/sysmacros.h instead of linux/kdev_t.h (vtrefny) - Resolves: rhbz#1644825 - -* Tue Oct 16 2018 Vojtech Trefny - 2.19-6 -- Fix 'Require exact version of the utils subpackage' (vtrefny) - Related: rhbz#1614328 - -* Tue Oct 16 2018 Vojtech Trefny - 2.19-5 -- Require exact version of the utils subpackage (vtrefny) - Related: rhbz#1614328 - -* Mon Oct 08 2018 Vojtech Trefny - 2.19-4 -- Use libblkid to check swap status before swapon (vtrefny) - Related: rhbz#1634016 -- Add error codes and Python exceptions for swapon fails (vtrefny) - Resolves: rhbz#1634016 - -* Mon Aug 13 2018 Vojtech Trefny - 2.19-3 -- Build VDO plugin on all architectures with VDO support (vtrefny) - Related: rhbz#1614328 - -* Mon Aug 13 2018 Vojtech Trefny - 2.19-2 -- Do not require 'dmraid' package (vtrefny) - Related: rhbz#1589861 - -* Fri Aug 10 2018 Vojtech Trefny - 2.19-1 -- Use python interpreter explicitly when running boilerplate_generator.py (vtrefny) -- vdo: Implement bd_vdo_get_stats() (tbzatek) -- Add test for is_tech_available with multiple dependencies (vtrefny) -- lvm-dbus.c: Check for 'lvmdbus' dependency in 'bd_lvm_is_tech_avail' (vtrefny) -- lvm.c: Check for 'lvm' dependency in 'bd_lvm_is_tech_avail' (vtrefny) -- Fix licence headers in sources (vtrefny) -- Fix three memory leaks in lvm-dbus.c (vtrefny) -- Ignore "bad-super-call" pylint warning in BlockDev.py (vtrefny) -- Fix running pylint in tests (vtrefny) -- Fix vdo configuration options definition in spec file (vtrefny) -- Fix calling BlockDev.reinit in swap tests (vtrefny) -- Fix how we check zram stats from /sys/block/zram0/mm_stat (vtrefny) -- Skip VDO tests also when the 'kvdo' module is not available (vtrefny) -- Add version to tests that should be skipped on CentOS/RHEL 7 (vtrefny) -- Skip btrfs tests if btrfs module is not available (vtrefny) -- Do not build KBD plugin with bcache support on RHEL (vtrefny) -- Do not build btrfs plugin on newer RHEL (vtrefny) -- fs: Properly close both ends of the pipe (tbzatek) -- Make sure library_test works after fixing -Wstrict-prototypes (vtrefny) -- Make sure library tests properly clean after themselves (vtrefny) -- pkg-config: add -L${libdir} and -I${includedir} (max.kellermann) -- plugins/kbd: make wait_for_file() static (max.kellermann) -- plugins/lvm{,-dbus}: get_lv_type_from_flags() returns const string (max.kellermann) -- plugins/dm: add explicit cast to work around -Wdiscarded-qualifiers (max.kellermann) -- plugins/crypto: work around -Wdiscarded-qualifiers (max.kellermann) -- plugins/check_deps: make all strings and `UtilDep` instances `const` (max.kellermann) -- exec: make `msg` parameters const (max.kellermann) -- fix -Wstrict-prototypes (max.kellermann) -- module.c: Accept kernel modules if they are built-in (marco.guerri.dev) -- BlockDev.py Convert dictionary keys to set before using them (vtrefny) -- Skip 'test_cache_pool_create_remove' on CentOS 7 (vtrefny) -- Re-order libbd_crypto_la_LIBADD to fix libtool issue (tom) -- acinclude.m4: Use AS_EXIT to fail in LIBBLOCKDEV_FAILURES (vtrefny) -- configure.ac: Fix missing parenthesis in blkid version check (vtrefny) -- Allow specifying extra options for PBKDF when creating LUKS2 (vtrefny) -- Reintroduce python2 support for Fedora 29 (vtrefny) -- Use versioned command for Python 2 (vtrefny) -- Fix few wrong names in doc strings (vtrefny) -- Make sure all our free and copy functions work with NULL (vtrefny) -- Use libblkid in bd_crypto_is_luks (vtrefny) -- vdo: Properly destroy the yaml parser (tbzatek) -- Add a simple test case for bd_crypto_tc_open (vtrefny) -- Add Python override for bd_crypto_tc_open_full (vtrefny) -- Show simple summary after configure (vtrefny) -- Do not build VDO plugin on non-x86_64 architectures (vtrefny) -- Sync spec with downstream (vtrefny) - -* Thu Jun 28 2018 Vojtech Trefny - 2.17-3 -- Build kbd plugin withou bcache support - -* Fri Jun 22 2018 Vojtech Trefny - 2.17-2 -- Do not build btrs plugin on RHEL 8 - -* Tue Apr 24 2018 Vojtech Trefny - 2.17-1 -- Redirect cryptsetup log to libblockdev log (vtrefny) -- Add a generic logging function for libblockdev (vtrefny) -- Add functions to resize LUKS 2 (vtrefny) -- Add function to get information about LUKS 2 integrity devices (vtrefny) -- Add function to get information about a LUKS device (vtrefny) -- Add a basic test for creating LUKS 2 format (vtrefny) -- Use libblockdev function to create LUKS 2 in tests (vtrefny) -- Add support for creating LUKS 2 format (vtrefny) -- Skip bcache tests on Rawhide (vtrefny) -- Allow building libblockdev without Python 2 support (vtrefny) -- Allow compiling libblockdev crypto plugin without escrow support (vtrefny) -- Require at least libndctl 58.4 (vtrefny) -- New function for luks metadata size (japokorn) -- Add functions to backup and restore LUKS header (vtrefny) -- Add function for killing keyslot on a LUKS device (vtrefny) -- Add functions to suspend and resume a LUKS device (vtrefny) -- Use '=' instead of '==' to compare using 'test' (v.podzimek) -- lvm-dbus: Check returned job object for error (vtrefny) -- Get sector size for non-block NVDIMM namespaces too (vtrefny) -- Fix memory leaks discovered by clang (vtrefny) -- Add new functions to docs/libblockdev-sections.txt (segfault) -- Make a link point to the relevant section (segfault) -- Don't use VeraCrypt PIM if compiled against libcryptsetup < 2.0 (segfault) -- Make keyfiles parameter to bd_crypto_tc_open_full zero terminated (segfault) -- Add function bd_crypto_device_seems_encrypted (segfault) -- Support VeraCrypt PIM (segfault) -- Support TCRYPT system volumes (segfault) -- Support TCRYPT hidden containers (segfault) -- Support TCRYPT keyfiles (segfault) -- Support unlocking VeraCrypt volumes (segfault) -- Enforce ZERO_INIT gcc backwards compatibility (bjornpagen) -- Add function for getting NVDIMM namespace name from devname or path (vtrefny) -- Add --without-xyz to DISTCHECK_CONFIGURE_FLAGS for disabled plugins (vtrefny) -- Add tests for the NVDIMM plugin (vtrefny) -- Add the NVDIMM plugin (vtrefny) -- Fix build with clang (bjornpagen) -- s390: don't hardcode paths, search PATH (flokli) -- Fix build against musl libc (bjornpagen) -- Fix python2-gobject-base dependency on Fedora 26 and older (vtrefny) -- Sync the spec file with downstream (vtrefny) - -* Wed Apr 11 2018 Vojtech Trefny - 2.16-3 -- Add the NVDIMM plugin (vtrefny) -- Add tests for the NVDIMM plugin (vtrefny) -- Add --without-xyz to DISTCHECK_CONFIGURE_FLAGS for disabled plugins (vtrefny) -- Add function for getting NVDIMM namespace name from devname or path (vtrefny) - -* Fri Feb 09 2018 Igor Gnatenko - 2.16-2 -- Escape macros in %%changelog - -* Thu Feb 08 2018 Vojtech Trefny - 2.16-1 -- Add tests for progress report (jtulak) -- Add e2fsck progress (jtulak) -- Add progress reporting infrastructure for Ext fsck (jtulak) -- Add a function to test if prog. reporting was initialized (jtulak) -- Add support for LUKS 2 opening and key management (vtrefny) -- Fix few more links for project and documentation website (vtrefny) -- Sync the spec file with downstream (vpodzime) -- Check if 'journalctl' is available before trying to use it in tests (vtrefny) -- Update 'Testing libblockdev' section in documentation (vtrefny) -- Fix link to online documentation (vtrefny) -- Fix how the new kernel module functions are added to docs (vpodzime) - -* Wed Feb 07 2018 Fedora Release Engineering - 2.15-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild - -* Wed Feb 07 2018 Iryna Shcherbina - 2.15-3 -- Update Python 2 dependency declarations to new packaging standards - (See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3) - -* Sat Feb 03 2018 Igor Gnatenko - 2.15-2 -- Switch to %%ldconfig_scriptlets - -* Fri Dec 01 2017 Vratislav Podzimek - 2.15-1 -- Do not use the 'btrfs' plugin in overrides tests (vpodzime) -- Do not use the btrfs plugin in library tests (vpodzime) -- Check for btrfs module availability in btrfs module (vtrefny) -- Move kernel modules (un)loading and checking into utils (vtrefny) -- Free locale struct in kbd plugin (vtrefny) -- Add test for setting partition flags on GPT (vtrefny) -- Use only sgdisk to set flags on GPT (vtrefny) -- Move the fs.h file to its original place (vpodzime) -- Add a HACKING.rst file (vpodzime) -- Mark bcache tests as unstable (vpodzime) -- Fix memory leaks in bd_fs_vfat_get_info() (vpodzime) -- Revert the behaviour of bd_fs_check_deps() (vpodzime) -- Split the bd_fs_is_tech_avail() implementation (vpodzime) -- Split the FS plugin source into multiple files (vpodzime) -- Fix bd_s390_dasd_format (vponcova) -- Mark unstable tests as such (vpodzime) -- bd_s390_dasd_is_ldl should be true only for LDL DADSs (vponcova) -- Do not lie about tag creation (vpodzime) - -* Wed Nov 08 2017 Zbigniew Jędrzejewski-Szmek - 2.14-2 -- Rebuild for cryptsetup-2.0.0 - -* Tue Oct 31 2017 Vratislav Podzimek - 2.14-1 -- Support the legacy boot GPT flag (intrigeri) -- Respect the version in the blockdev.pc file (vpodzime) -- Add pkgconfig definitions for the utils library (vpodzime) -- fs.c: Fix potential NULL pointer dereference (vtrefny) -- dm.c: Fix uninitialized values in various dm plugin functions (vtrefny) -- dm.c: Check return values of dm_task_set_name/run/get_info functions (vtrefny) -- fs.c: Fix multiple "forward NULL" warnings in 'bd_fs_ntfs_get_info' (vtrefny) -- lvm-dbus.c: Fix multiple "use after free" coverity warnings (vtrefny) -- Fix duplicate 'const' in generated functions (vtrefny) -- Add some test cases for NTFS (kailueke) -- Add function wrappers for NTFS tools (kailueke) -- exec.c: Fix error message in 'bd_utils_exec_and_report_progress' (vtrefny) -- crypto.c: Fix waiting for enough entropy (vtrefny) -- Ignore some coverity false positive errors (vtrefny) -- exec.c: Ignore errors from 'g_io_channel_shutdown' (vtrefny) -- part.c: Check if we've found a place to put new logical partitions (vtrefny) -- kbd.c: Fix potential string overflow in 'bd_kbd_bcache_create' (vtrefny) -- exec.c: Fix resource leaks in 'bd_utils_exec_and_report_progress' (vtrefny) -- fs.c: Fix "forward null" in 'do_mount' and 'bd_fs_xfs_get_info' (vtrefny) -- part.c: Fix possible NULL pointer dereference (vtrefny) -- crypto.c: Use right key buffer in 'bd_crypto_luks_add_key' (vtrefny) -- exec.c: Fix "use after free" in 'bd_utils_check_util_version' (vtrefny) -- kbd.c: Fix double free in 'bd_kbd_zram_get_stats' (vtrefny) -- part.c: Check if file discriptor is >= 0 before closing it (vtrefny) -- mdraid.c: Fix resource leaks (vtrefny) -- lvm.c: Fix "use after free" in 'bd_lvm_get_thpool_meta_size' (vtrefny) -- fs.c: Fix for loop condition in 'bd_fs_get_fstype' (vtrefny) -- fs.c: Check sscanf return value in 'bd_fs_vfat_get_info' (vtrefny) -- fs.c: Fix resource leaks in 'bd_fs_get_fstype' (vtrefny) -- blockdev.c.in: Fix unused variables (vtrefny) -- Use libbytesize to parse bcache block size (vtrefny) -- Use system values in KbdTestBcacheStatusTest (vtrefny) -- Fix BSSize memory leaks in btrfs and mdraid plugins (vtrefny) -- Skip btrfs subvolume tests with btrfs-progs 4.13.2 (vtrefny) -- Added function to get DM device subsystem (japokorn) -- Sync spec with downstream (vpodzime) - -* Fri Sep 29 2017 Vratislav Podzimek - 2.13-1 -- Fix the rpmlog and shortlog targets (vpodzime) -- Add a function for enabling/disabling plugins' init checks (vpodzime) -- Assign functions to tech-mode categories (vpodzime) -- Add missing items to particular sections in the documentation (vpodzime) -- Add a basic test for the runtime dependency checking (vpodzime) -- Simplify what WITH_BD_BCACHE changes in the KBD plugin (vpodzime) -- Add functions for querying available technologies (vpodzime) -- Dynamically check for the required utilities (vpodzime) -- Use shorter prefix for tempfiles (vtrefny) -- Try harder when waiting for lio device to show up (vtrefny) -- Better handle old and new zram sysfs api in tests (vtrefny) -- Skip btrfs tests on CentOS 7 aarch64 (vtrefny) -- Add new function for setting swap label (vtrefny) -- Use only one git tag for new releases (vtrefny) -- Fix source URL in spec file (vtrefny) -- Add NEWS.rst file (vtrefny) -- Do not include s390utils/vtoc.h in s390 plugin (vtrefny) -- Use "AC_CANONICAL_BUILD" to check architecture instead of "uname" (vtrefny) -- Bypass error proxy in s390 test (vtrefny) -- Fix zFCP LUN max length (vtrefny) -- Do not run g_clear_error after setting it (vtrefny) -- Allow compiling libblockdev without s390 plugin (vtrefny) -- Add a function for getting plugin name (vpodzime) - -* Thu Sep 28 2017 Troy Dawson - 2.12-3 -- Cleanup spec file conditionals correctly - -* Wed Sep 27 2017 Troy Dawson - 2.12-2 -- Cleanup spec file conditionals - -* Wed Aug 30 2017 Vratislav Podzimek - 2.12-1 -- Own directories /etc/libblockdev and /etc/libblockdev/conf.d (vtrefny) -- Wait for resized partition (kailueke) -- Make sure the device is opened for libparted (vpodzime) -- Fix label check in swap_test (vtrefny) -- Use "run_tests" script for running tests from Makefile (vtrefny) -- Add a script for running tests (vtrefny) -- Tests: Move library initialization to setUpClass method (vtrefny) -- Stop skipping FAT resize tests on rawhide (vtrefny) -- Close filesystem before closing the partition during FAT resize (vtrefny) -- Use mountpoint for "xfs_info" calls (vtrefny) -- Use libmount cache when parsing /proc/mounts (vtrefny) -- Add some space for the CI status (vpodzime) -- Confirm the force when creating PVs in FS tests (vpodzime) -- Skip vgremove tests on 32bit Debian (vtrefny) -- Fix names of backing files in tests (vtrefny) -- Fix checking for available locales (vtrefny) -- Skip dependency checking in mpath tests on Debian (vtrefny) -- Skip zRAM tests on Debian (vtrefny) -- Skip the test for device escrow on Debian too (vtrefny) -- Skip free region tests on Debian too (vtrefny) -- Fix redirecting command output to /dev/null in tests (vtrefny) -- Try harder to unmount devices in test cleanup (vtrefny) -- Require only plugins that are needed for given test (vtrefny) -- Try to get distribution info from "PrettyName" if "CPEName" isn't available (vtrefny) -- Use -ff when creating PVs in FS tests (vpodzime) -- Sync spec with downstream (vpodzime) - -* Mon Jul 31 2017 Vratislav Podzimek - 2.11-1 -- Make the KbdZRAMDevicesTestCase inherit from KbdZRAMTestCase (vpodzime) -- Allow non-source directory builds (kailueke) -- Add a way to disable runtime dependency checks (vpodzime) -- Link to GObject even if no plugin is activated (kailueke) -- Skip zram tests on Rawhide (vpodzime) -- Keep most utilities available for tests (vpodzime) -- Use new libmount function to get (un)mount error message (vtrefny) -- Update the documentation URL (vpodzime) - -* Wed Jul 26 2017 Fedora Release Engineering - 2.10-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild - -* Wed Jul 05 2017 Vratislav Podzimek - 2.10-1 -- Only enable partition size tolerance with alignment (vpodzime) -- Limit the requested partition size to maximum possible (vpodzime) -- Do not verify vfat FS' size after generic resize (vpodzime) -- Specify tolerance for partition size (kailueke) -- Only use the exact constraint if not using any other (vpodzime) -- Check resulting FS size in tests for generic FS resize (vpodzime) -- Query setting FS label support and generic relabeling (kailueke) -- Do not strictly require all FS utilities (vpodzime) -- Compile everything with the C99 standard (vpodzime) -- Add partition resize function (kailueke) -- Generic Check and Repair Functions (kailueke) -- Query functions for FS resize and repair support (kailueke) -- Update the project/source URL in the spec file (vpodzime) -- Add functions for opening/closing TrueCrypt/VeraCrypt volumes (vpodzime) -- Adapt to a change in behaviour in new libmount (vpodzime) -- Try RO mount also if we get EACCES (vpodzime) -- Size in bytes for xfs_resize_device (kailueke) -- src/plugins/Makefile.am: Remove hard coded include path in /usr prefix (tristan.vanberkom) -- Fixed include for libvolume_key.h (tristan.vanberkom) -- Ignore parted warnings if possible (squimrel) -- bcache tests: Remove FEELINGLUCKY checks (tasleson) -- kbd.c: Code review corrections (tasleson) -- kbd.c: Make bd_kbd_bcache_create work without abort (tasleson) - -* Tue Jun 13 2017 Vratislav Podzimek - 2.9-1 -- Fix hardcoded reference to gcc (timo.gurr) -- Catch and ignore partial failures in LVM tests' cleanups (vpodzime) -- Fix hardcoded reference to pkg-config (timo.gurr) -- Make GObject introspection optional (vpodzime) -- Do not link libraries required by plugins to the GI files (vpodzime) -- Make sure the whole build status image is shown (vpodzime) -- Show CI status in README (at the GH repo's front page) (vpodzime) -- Always require the libudev pkg (tgurr) -- Make sure we give kernel time to fully setup zram device(s) (vpodzime) -- fs_test.py: Close FDs when calling utilities (tasleson) -- crypto.c: Correct segmentation fault (tasleson) - -* Tue Jun 06 2017 Vratislav Podzimek - 2.8-1 -- Temporarily skip vfat generic resize test on rawhide (vtrefny) -- Use "safeprobe" in "bd_fs_wipe" (vtrefny) -- Add a generic filesystem resize function (vtrefny) -- Add a function to get mountpoint for a device (vtrefny) -- Add a function to get filesystem type for a device (vtrefny) -- Only include the LVM DBus config when shipping LVM DBus (vpodzime) -- Skip the LVM DBus vgreduce tests on Rawhide (vpodzime) -- Do not build the lvm-dbus plugin on RHEL/CentOS (vpodzime) -- Give zRAM more time to settle before trying to remove a device (vpodzime) -- Put zram tests adding/removing devices into a separate class (vpodzime) -- Skip LVM cache and RAID tests on Rawhide (vpodzime) -- Fix the skip_on decorator factory for tests (vpodzime) -- Use 'blkid -p' instead of lsblk to get device's FS type (vpodzime) -- Improve the lvm_set_global_config test (vpodzime) -- Pass '-y' to pvresize (vpodzime) -- Create a copy of os.environ for a child process (vpodzime) -- Revert "Use the "C.UTF-8" locale instead of just "C"" (vpodzime) -- Fix how we create vfat FS in tests (vpodzime) -- Skip the test if requiring unavailable locales (vpodzime) -- Use the "C.UTF-8" locale instead of just "C" (vpodzime) -- Add functions for working with ext2 and ext3 filesystems (vtrefny) -- Link to gobject when lvm or btrfs is enabled (andreas) -- Link to libm where needed (andreas) -- Add a function for cleaning a device (vtrefny) -- Add few code exaples to documentation (vtrefny) -- Use a special exception for no fs signature when doing wipe (vpodzime) -- One more incompatible os.symlink() call (vpodzime) -- Do not use pass-by-name in the os.symlink() call (vpodzime) -- Ignore previous errors when falling back to using ioctl() (vpodzime) -- Use ioctl() call to teardown loop devices (vpodzime) -- Resolve the device to remove for mdadm (vpodzime) -- Add a function for getting device symlinks (vpodzime) -- Use the new resolve_device() function where appropriate (vpodzime) -- Add the resolve_device() function to the utils library (vpodzime) -- First try to read the 'autoclear' flag from /sys/ (vpodzime) - -* Wed Apr 26 2017 Vratislav Podzimek - 2.7-1 -- Skip btrfs min size tests on Fedora 25 (vtrefny) -- Make sure the loop device doesn't disappear during tests (vpodzime) -- Close the loop device when autoclear is (un)set (vpodzime) -- Do not enforce Python 3 for running tests in CI (vpodzime) -- Revert "Use different BTRFS_MIN_MEMBER_SIZE on aarch64" (vtrefny) -- Use both 'old' and 'new' sysfs files to read zRAM stats (vtrefny) -- Check if libparted-fs-resize pkgconfig is available (vpodzime) -- Do not try to get name for inactive partitions (vtrefny) -- Skip tests for getting free regions on CentOS/RHEL (vpodzime) -- Free the container holding the specs of free regions (vpodzime) -- Open loop devices as O_RDONLY when getting flags (vpodzime) -- Resolve maximum partition size when we know its start (vpodzime) -- Use --id instead of --part-type when setting partition id (vpodzime) -- Fix mdadm command for removing failed device from an array (vtrefny) -- Skip bcache tests on CentOS/RHEL 7 (vpodzime) -- Use six.assertRaisesRegex in the FS tests (vpodzime) -- Use mkdtemp() instead of TemporaryDirectory() (vpodzime) -- Fix installation without specifying --exec-prefix (vpodzime) -- Add options to force mkfs.ext4/vfat to create a FS on the whole device (vpodzime) -- Skip the test for device escrow on CentOS/RHEL (vpodzime) -- Define DEVNULL on our own if not in subprocess (vpodzime) -- Remove the patches from the spec file (vpodzime) -- Sync the spec file with downstream (vpodzime) -- Stop skipping zRAM stats tests (vtrefny) -- Add more tests for zRAM stats (vtrefny) -- Fix reading zRAM properties from sysfs (vtrefny) - -* Wed Apr 12 2017 Vratislav Podzimek - 2.6-3 -- Do not try to parse 'raid_spec' for 'bd_md_activate' (vtrefny) - Resolves: rhbz#1439111 - -* Tue Apr 11 2017 Vratislav Podzimek - 2.6-2 -- Make sure the returned thpool MD size is valid (vpodzime) - -* Wed Mar 15 2017 Vratislav Podzimek - 2.6-1 -- Move the part_err library before part and fs (vtrefny) -- Fix BuildRequires for crypto and dm packages (vtrefny) -- Fix mounting read-only devices (vtrefny) -- Fix the bd_s390_dasd_is_ldl function. (vponcova) -- Add the bd_s390_dasd_is_fba function to check if DASD is FBA (vponcova) -- Disable MD RAID tests on 32bit systems (vpodzime) -- Fix error message when mounting with a wrong fs type (vtrefny) -- Only create RPMs for requested/configured plugins (vpodzime) -- Only check dependencies of plugins to be built (vpodzime) -- Only build and distribute plugins if configured so (vpodzime) -- Fix format-security and unused-result compiler warnings (vtrefny) -- Add an AC macro for modular builds (vpodzime) -- Add functions for mounting and unmounting filesystems (vtrefny) - -* Mon Mar 06 2017 Vratislav Podzimek - 2.5-1 -- Do not try to get GVariant after not adding anything to its builder (vpodzime) -- Replace NULL with "" when building ExtraArg (vpodzime) -- Replace NULL with "" when adding it as a 's' GVariant (vpodzime) -- Make sure we don't try to add NULL as GVariant to DBus params (vpodzime) -- Add function for getting recommended thpool metadata size (vpodzime) -- Make udev settle after we create a LIO device (vpodzime) -- Always use '--yes' for lvremove (vpodzime) - -* Tue Feb 21 2017 Vratislav Podzimek - 2.4-1 -- Update specs.rst to use present-tense and current API (agrover) -- Add functions using BLOBs as LUKS passphrases (vpodzime) -- Make sure the _error_quark() functions are in the library (vtrefny) -- Return a special error when trying to wipe empty device (vtrefny) -- Adapt tests to use LIO devices instead of loop devices (vpodzime) -- Add functions for creating and deleting LIO devices (vpodzime) -- MDRAID: Allow path as input for functions that work with sysfs (vtrefny) - -* Wed Feb 15 2017 Vratislav Podzimek - 2.3-3 -- Rebuild with changelog fixed up - -* Tue Feb 14 2017 Vratislav Podzimek - 2.3-1 -- Allow specifying raid 'name' in multiple way when calling md functions (vtrefny) -- Allow using both path and raid name in bd_md_set_bitmap_location (vtrefny) -- Fix potential memory issues in s390 sanitizate functions (vpodzime) -- Try multiple times when probing device for wiping (vpodzime) -- Check for libvolume_key.h and dmraid.h in configure.ac (vpodzime) -- Define our own macro for testing required header files (vpodzime) -- Include blockdev/utils.h in mdraid.h (vtrefny) -- Fix misspelling (agrover) -- Skip the bcache tests even on x86_64 (vpodzime) -- Take a break between bcache creation tests (vpodzime) -- Make sure ./configure fails if there are some soft failures (vpodzime) -- Improve the error message on missing GI support (vpodzime) -- Only require bcache-tools if supporting bcache (vpodzime) -- Skip bcache tests on non-x86_64 architectures (vpodzime) -- Try harder to register a new bcache device (vpodzime) -- Reimplement swapon/swapoff functions by using syscalls (vpodzime) -- Make sure bcache functions are correctly provided or not (vpodzime) -- Changelog fixup (vpodzime) - -* Fri Feb 10 2017 Fedora Release Engineering - 2.2-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild - -* Wed Jan 11 2017 Vratislav Podzimek - 2.2-2 -- Rebuild with changelog fixed up - -* Wed Jan 11 2017 Vratislav Podzimek - 2.2-1 -- Use the .in file as source when bumping version (vpodzime) -- Run pylint based on the python version and make it optional (vpodzime) -- Disable python3 and bcache on RHEL (vpodzime) -- Make bcache support optional (vpodzime) -- Teach boileplate_generator.py to skip things based on patterns (vpodzime) -- Require lower versions of some utilities (vpodzime) -- Do not require python3 for the boilerplate generation script (vpodzime) -- Use a proper initialization value for 'GPollFD fds[2]' (vpodzime) -- Deal with older parted and libblkid (vpodzime) -- Make python3 and gtk-doc optional (vpodzime) -- Bump the version of the utils library (vpodzime) -- Fix docstring for 'bd_md_node_from_name' (vtrefny) -- Add tests for added mdraid methods (vtrefny) -- Skip 'MDTestNominateDenominateActive' unless feeling lucky (vtrefny) -- MDRaid tests: change 'wait_for_resync' to wait for given action (vtrefny) -- Add functionality need by storaged to mdraid plugin (vtrefny) -- Move 'echo_str_to_file' method to utils (vtrefny) -- Add a function to setup a loop device from a file descriptor (vpodzime) -- Add functions to get/set the autoclear flag on a loop device (vpodzime) -- Fix checking /proc/mdstat for resync action (vtrefny) -- Adapt the test config files to version 2.x (vpodzime) - -* Mon Dec 12 2016 Charalampos Stratakis - 2.1-3 -- Rebuild for Python 3.6 - -* Tue Nov 15 2016 Vratislav Podzimek - 2.1-2 -- Rebuild for a chain-build with storaged (vpodzime) - -* Thu Nov 10 2016 Vratislav Podzimek - 2.1-1 -- Do not require an exclusive lock on the device to commit part stuff (vpodzime) -- Prevent failure if there are no btrfs subvolumes (vpodzime) -- Fix the test for getting version of a failing utility (vpodzime) -- Also run the utils tests (vpodzime) -- Bump the version of the pkgconfig module (vpodzime) -- Include utils.h in plugins that need it (vpodzime) -- Fix dependency check in fs plugin (vtrefny) -- Add support for setting part id (part type) on msdos part tables (vtrefny) -- Trim the extra info for MD RAID's name (vpodzime) -- Add xfsprogs and dosfstools as dependencies of the fs plugin (vpodzime) -- Fix md_name_from_node to work with the "/dev/" prefix (vpodzime) -- New major upstream release - -* Wed Nov 9 2016 Vratislav Podzimek - 1.9-8 -- Revert "Prevent issues between libparted and udev" (vpodzime) -- Revert "Open the device file as RDWR when committing parts" (vpodzime) - -* Thu Oct 27 2016 Vratislav Podzimek - 1.9-7 -- Open the device file as RDWR when committing parts (vpodzime) -- Handle mdadm --examine output during migration (adamw) - Resolves: rhbz#1381996 - -* Mon Oct 24 2016 Vratislav Podzimek - 1.9-6 -- Prevent issues between libparted and udev (vpodzime) - -* Mon Oct 10 2016 Vratislav Podzimek - 1.9-5 -- Make sure all object paths are passed and extracted as such (vpodzime) - Resolves: rhbz#1374973 - -* Tue Oct 4 2016 Vratislav Podzimek - 1.9-4 -- Do not report volume name for FW RAID container device (vpodzime) - Related: rhbz#1379865 -- Search for just "UUID" in mdadm --examine output (vpodzime) - Related: rhbz#1379865 -- Use 'mdadm --examine --export' to get MD RAID level (vpodzime) - Related: rhbz#1379865 - -* Mon Oct 3 2016 Vratislav Podzimek - 1.9-3 -- Try to search for "RAID Level" in mdadm's output (vpodzime) - Resolves: rhbz#1379865 -- Fix the number passed to LVM DBus as a job-creation timeout (vpodzime) - Resolves: rhbz#1378970 - -* Mon Aug 29 2016 Vratislav Podzimek - 1.9-2 -- Explicitly cast number constants for GVariants (vpodzime) - -* Wed Jul 27 2016 Vratislav Podzimek - 1.9-1 -- Add functions for creating thin/cache pools from existing LVs (vpodzime) -- Add the new mpath_get_members() function to the docs (vpodzime) - -* Tue Jul 19 2016 Fedora Release Engineering - 1.8-2 -- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages - -* Wed Jun 29 2016 Vratislav Podzimek - 1.8-1 -- Add a function to get all mpath member devices (vpodzime) -- Fix backport issues in the zfcp-related functionality (#1348442) (vpodzime) -- Revert "Fix a few const params in the s390 plugin." (vpodzime) -- Fix creation of the version-only tags (vpodzime) - -* Wed Jun 15 2016 Vratislav Podzimek - 1.7-1 -- Include the LV roles in the LVdata (vpodzime) -- Add a few missing items to the documentation (vpodzime) -- Document fields of the structures (vpodzime) -- Report (meta)data LV name properly for cache pools in lvm-dbus (vpodzime) -- Add information about related LVs to LVMLVdata (vpodzime) -- Remove unused code for getting supported functions (vpodzime) -- Add zFCP functionality to s390 plugin (sbueno+anaconda) -- Fix a few const params in the s390 plugin. (sbueno+anaconda) - -* Wed Jun 01 2016 Vratislav Podzimek - 1.6-1 -- Ignore merge commits when creating changelog (vpodzime) -- Only take the number of the first %%changelog line found (vpodzime) -- Add some more detailed description to the part plugin (vpodzime) -- Fix a few extra issues with the const types (vpodzime) -- Add function for getting best free region (vpodzime) -- Add function for getting free regions (vpodzime) -- Fix the error message when setting part flag fails (vpodzime) -- Add function for setting disk flags (vpodzime) -- Add function for getting information about disk(s) (vpodzime) -- Do not set nonsense partition paths (vpodzime) -- Add function for getting partition by position (vpodzime) -- Indicate if there was error from parted or not in set_parted_error() (vpodzime) -- Minor fixes for the bd_part_get_part_spec() function (vpodzime) -- Add support for extra GPT flags (vpodzime) -- Add functionality for partition types (GUIDs) (vpodzime) -- Add functionality for partition names (vpodzime) -- Do not destroy disk objects we didn't get (vpodzime) -- Add a function for setting multiple partition flags at once (vpodzime) -- Remove the unused definition USE_PYTHON3 from configure.ac (vpodzime) -- Use different BTRFS_MIN_MEMBER_SIZE on aarch64 (vpodzime) -- Better release memory from parted objects on failures (vpodzime) -- Rework how we do optimal alignment (vpodzime) -- Do not try to destroy object we didn't get (vpodzime) -- Don't pass sizes in bytes to LVM (#1317373) (vpodzime) -- Add the libbytesize-devel build requires (vpodzime) -- Search for the LVM DBus service in both active and activatable names (vpodzime) -- Adapt to another stupid change in btrfs-progs (vpodzime) -- Add the XFS-related functions to the documentation (vpodzime) -- Add tests for the XFS-related functions (vpodzime) -- Add support for the XFS file system to the FS plugin (vpodzime) -- Add chunk_size to BDMDExamineData (vtrefny) -- Add the subpackage for the FS plugin (vpodzime) -- Add the FS plugin to the docs (vpodzime) -- Add tests for the ext4 functionality in the fs plugin (vpodzime) -- Add the FS plugin and the ext4 support (vpodzime) -- Add a function for running utility reporting error and exit code (vpodzime) -- Add the subpackage for the part plugin (vpodzime) -- Add a missing BuildRequires for parted-devel (vpodzime) -- Tag as both libblockdev-$version and just $version (vpodzime) -- Add the 'part' plugin to documentation (vpodzime) -- Add tests for the newly added part plugin (vpodzime) -- Add the part plugin with storaged-required functionality (vpodzime) - -* Mon Mar 21 2016 Vratislav Podzimek - 1.5-1 -- Merge pull request #72 from vpodzime/master-faster_tests (vpodzime) -- Ignore all .bak files (vpodzime) -- Use python3-pylint and skip Python 2 tests (vpodzime) -- Try a bit harder when deactivating MD arrays in tests (vpodzime) -- Recompile only the LVM plugin in tests (vpodzime) -- Merge pull request #65 from vpodzime/master-loc_indep_error (vpodzime) -- Merge pull request #70 from vojtechtrefny/master-chunk_size (vpodzime) -- Add bd_md_create_with_chunk_size() function (vtrefny) -- Merge pull request #68 from vpodzime/master-no_intro_data (vpodzime) -- Merge pull request #71 from vpodzime/master-ipython3 (vpodzime) -- Run coverage with the right config directories (vpodzime) -- Merge pull request #67 from phatina/master (vpodzime) -- Merge pull request #69 from vpodzime/master-lvm_dbus_autostart (vpodzime) -- Use ipython3 for debugging and testing sessions (vpodzime) -- Don't expect to always get introspection data from DBus (vpodzime) -- Make invocation of tests configurable (phatina) -- Make error messages locale agnostic (vpodzime) - -* Tue Mar 15 2016 Vratislav Podzimek - 1.4-5 -- Search for the LVM DBus service in activatable names (vpodzime) -- Better check for the LVM DBus API (vpodzime) - -* Wed Mar 9 2016 Vratislav Podzimek - 1.4-4 -- Do not try to get object path of NULL in vgreduce (vpodzime) - -* Tue Mar 1 2016 Peter Robinson 1.4-3 -- Depend on python3-gobject-base not python3-gobject so as to not pull in X components - -* Thu Feb 25 2016 Vratislav Podzimek - 1.4-2 -- Add/fix the requirement for the LVM DBus daemon - -* Thu Feb 25 2016 Vratislav Podzimek - 1.4-1 -- Merge pull request #62 from vpodzime/master-clean_up (vpodzime) -- Use addCleanup() instead of tearDown() in tests (vpodzime) -- Merge pull request #58 from vpodzime/master-lvm_dbus_pr (vpodzime) -- Add the VG renaming functionality (vpodzime) -- Packaging of the lvm-dbus plugin (vpodzime) -- The LVM DBus plugin (vpodzime) -- Add more generic functions for logging (vpodzime) -- Use MAX(a, b) instead of CLAMP(b, a, b) (vpodzime) -- Merge pull request #59 from vpodzime/master-vgrename (vpodzime) -- Add a function for renaming VGs (vpodzime) -- Merge pull request #57 from clumens/master (vpodzime) -- Fix error reporting when running "make test". (clumens) -- Merge pull request #54 from vojtechtrefny/master-pvsize (vpodzime) -- Do not try to create a PV with 4KiB metadata space (vpodzime) -- Add pv_info to BDLVMPVdata (vtrefny) -- btrfs now requires at least 128MiB device(s) (vpodzime) -- Merge pull request #52 from vpodzime/master (vpodzime) -- Round size in thpoolcreate() to KiB (vpodzime) -- Sync the %%changelog in spec with downstream (vpodzime) - -* Wed Nov 25 2015 Vratislav Podzimek - 1.3-4 -- Create the cache pool before the to-be-cached LV (vpodzime) - -* Thu Nov 05 2015 Robert Kuska - 1.3-3 -- Rebuilt for Python3.5 rebuild - -* Wed Nov 04 2015 Vratislav Podzimek - 1.3-2 -- Fix the annotation of bd_try_init in blockdev.c (vpodzime) - -* Mon Oct 26 2015 Vratislav Podzimek - 1.3-1 -- Add missing python GI requires (vpodzime) -- Merge pull request #49 from dashea/libblockdev-python (vpodzime) -- Merge pull request #50 from vpodzime/master-fix_striped_lv (vpodzime) -- Merge pull request #46 from vpodzime/master-bcache_destroy (vpodzime) -- Merge pull request #39 from vpodzime/master-lvm_physical_space (vpodzime) -- Add a missing ldconfig that rpmlint found. (dshea) -- Move python files to separate packages (#1256758) (dshea) -- Fix lvcreate calls for striped LVs (vpodzime) -- Merge pull request #48 from vojtechtrefny/master_pvfree (vpodzime) -- Add pv_free to BDLVMPVdata (vtrefny) -- Merge pull request #47 from atodorov/add_coverage_report (vpodzime) -- Produce coverage report in CI (atodorov) -- Check bcache device's state before trying to detach the cache in destroy() (vpodzime) -- Fix URLs in the spec (vpodzime) -- Fix the int-float less-than comparison (vpodzime) -- Fix the calculation of physical space taken by an LV (vpodzime) - -* Wed Sep 23 2015 Vratislav Podzimek - 1.2-1 -- Merge pull request #40 from vpodzime/master-config_support (vpodzime) -- Add tests for configuration support (vpodzime) -- Add a function for getting the loaded soname for a plugin (vpodzime) -- Add the default configuration (vpodzime) -- Load and respect configuration files when loading plugins (vpodzime) -- Add functions for finding and processing configuration files (vpodzime) -- Merge pull request #38 from vpodzime/master-md_superblock_size (vpodzime) -- Better document how MD RAID superblock size should be calculated (vpodzime) -- Merge pull request #36 from phatina/master (vpodzime) -- BTRFS: allow an arbitrary label to be set for a btrfs volume (phatina) -- Merge pull request #32 from phatina/master (vpodzime) -- BTRFS: fix parsing empty partition label (phatina) -- Merge pull request #35 from vpodzime/master (vpodzime) -- Define env variables for sudo via the env utility (vpodzime) -- Merge pull request #34 from dashea/python3-tests (vpodzime) -- Use unittest.addCleanup to simplify crypto_test. (dshea) -- Run tests with both python2 and python3 in the ci target. (dshea) -- Fix python3 issues in the unittests. (dshea) -- Do not run all tests in the 'ci' target (vpodzime) -- Merge pull request #33 from clumens/master (vpodzime) -- Add a new makefile target that does everything needed for jenkins. (clumens) -- Synchronize the .spec file with downstream (vpodzime) - -* Fri Jul 24 2015 Vratislav Podzimek - 1.1-2 -- Explicitly specify the type of the cert_data parameter (#1246096) (vpodzime) - -* Fri Jun 19 2015 Vratislav Podzimek - 1.1-1 -- Clean generated boilerplate code on 'make clean' (vpodzime) -- Merge pull request #31 from atodorov/use_lang_c (vpodzime) -- tests: use LANG=C in test_backup_passphrase() (atodorov) -- Merge pull request #30 from atodorov/makefile_updates (vpodzime) -- Makefile.am: - add separate check target - add coverage targets - make it possible to test with Python3 (atodorov) -- Merge pull request #29 from atodorov/fix_issue_28 (vpodzime) -- Merge pull request #27 from atodorov/fix_docs_url (vpodzime) -- Merge pull request #26 from atodorov/test_docs (vpodzime) -- Change the modified sources back in tearDown() method as well. Closes #28. (atodorov) -- update URL to on-line documentation (atodorov) -- add test documentation (atodorov) -- Merge pull request #22 from dashea/escrow-tests (vpodzime) -- Merge pull request #25 from dashea/python-dep (vpodzime) -- Filter the python files from automatic rpm requires (dshea) -- Added tests for escrow packets and backup passphrases (dshea) -- Free leaked contexts from crypto_init (dshea) -- Cooperate with volume_key's memory management (dshea) -- Fix inheritance in the LVM tests to prevent multiple runs of some tests (vpodzime) -- Make the regexp for testing crypto_generate_backup_passphrase() stricter (vpodzime) -- Leave room in the backup passphrase for a trailing 0 (dshea) -- Add functions to get names of data/metadata internal LVs (vpodzime) -- Allow getting info for an internal LV (vpodzime) -- Gather information about all LVs (vpodzime) -- Round requested size to KBs in lvresize() (#1221247) (vpodzime) -- Add overrides for the ensure_init() function (vpodzime) -- Change the default value of the 'reload' parameter of try_reinit() (vpodzime) -- Merge pull request #21 from vpodzime/master-thpool_size_discard (vpodzime) -- Add overrides for the lvm_is_valid_thpool_chunk_size() function (vpodzime) - -* Wed Jun 17 2015 Fedora Release Engineering - 1.0-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild - -* Thu May 21 2015 Vratislav Podzimek - 1.0-1 -- Adapt the release helper targets to autotools (vpodzime) -- Fixes of paths in Makefile.am's inspired by build failures on s390 (vpodzime) -- Add an s390-specific BuildRequires (vpodzime) -- Distribute also the boilerplate_generator.py script (vpodzime) -- Fix path to the generated blockdev.pc file (vpodzime) -- Adapt tests that compile stuff to autotools (vpodzime) -- Merge pull request #18 from vpodzime/master-autotools (vpodzime) -- Merge pull request #20 from dashea/gtkdoc-sections (vpodzime) -- Use the autotools building system instead of scons (vpodzime) -- Add the two new functions to the 'blockdev' docs section (vpodzime) -- Fix the line defining the docs file for the s390 section (vpodzime) -- Add a missing #include to the kbd.api file (vpodzime) -- Prevent s390-specific stuff from being used on other architectures (vpodzime) -- Update the documentation of the is_initialized() function (vpodzime) -- Merge pull request #19 from vpodzime/master-ensure_init (vpodzime) -- Remove private macros from the gtkdoc sections file. (dshea) -- Terminate ifdef statements for arch check. (sbueno+anaconda) -- Return early from the init functions if setting up logging fails (vpodzime) -- Add tests for the new and modified init functions (vpodzime) -- Add new try_init() and try_reinit() functions (vpodzime) -- Fix for adding number of loaded plugins (vpodzime) -- Fix for ensure_init() (vpodzime) -- Rename the try_init() function to ensure_init() and improve it (vpodzime) -- Check number of loaded plugins and library initialization state (vpodzime) -- Make 'reload' default to True instead of False in overrides (vpodzime) -- Add the s390 plugin test file. (sbueno+anaconda) -- Add the s390 plugin functions. (sbueno+anaconda) -- Add the s390 plugin. (sbueno+anaconda) -- Fix a typo in the spec file. (sbueno+anaconda) -- Require the kmod-devel package for the build process (vpodzime) -- Merge pull request #16 from dashea/escrow-encoding (vpodzime) -- Merge pull request #13 from vpodzime/master-lvm_cache (vpodzime) -- Merge pull request #12 from vpodzime/master-kbd_plugin (vpodzime) -- Merge pull request #14 from vpodzime/master-better_is_multipath (vpodzime) -- Use g_strdup() instead of g_strdup_printf() to just dup a string (vpodzime) -- Fix the spelling of "escrow" (dshea) -- Make the crypto plugin string parameters const (dshea) -- Set encoding to NULL before writing the escrow packet. (dshea) -- Get cache stats directly from the device mapper (vpodzime) -- Reimplement the is_mpath_member() function using device mapper (vpodzime) -- Add the LVM cache related symbols to the LVM section in the documentation (vpodzime) -- Update the list of LVM cache related functions in features.rst (vpodzime) -- Add tests for functions related to the LVM cache technology (vpodzime) -- Implement the lvm_cache_stats() function (vpodzime) -- Implement the lvm_cache_pool_name function (vpodzime) -- Implement the lvm_cache_create_cached_lv() function (vpodzime) -- Implement lvm_cache_attach/detach() functions (vpodzime) -- Implement the lvm_cache_create_pool() function plus two support functions (vpodzime) -- Implement the lvm_cache_get_default_md_size() function (vpodzime) -- Add the 'type' parameter to the lvm_lvcreate function (vpodzime) -- Teach boilerplate_generator to work with enum return types (vpodzime) -- Teach boilerplate_generator to work with 'const' return types (vpodzime) -- Add subpackages for the KBD plugin and its devel files (vpodzime) -- Add provided symbols to the documentation section of the KBD plugin (vpodzime) -- Implement the bcache_get_backing/cache_device functions (vpodzime) -- Exclude bcache tests from the normal 'test' target (vpodzime) -- Add some more and prolong some of the waits in KBD tests (vpodzime) -- Zero all newly allocated structures (vpodzime) -- Implement the bcache_status function and all it wants (vpodzime) -- Fix for the zram stats (vpodzime) -- Add bcache_get_mode and bcache_set_mode functions (vpodzime) -- Teach boilerplate_generator to work with enum return types (vpodzime) -- Teach boilerplate_generator to work with 'const' return types (vpodzime) -- Add the zram_get_stats function (vpodzime) -- Add the check() function for the KBD plugin (vpodzime) -- Add ErrorProxy instance for the KBD plugin (vpodzime) -- Add tests for bcache_create/attach/detach/destroy functions (vpodzime) -- Add the 'rebuild' Makefile target (vpodzime) -- Add bcache_create, bcache_attach, bcache_detach and bcache_destroy functions (vpodzime) -- Implement a helper function to echo string into a file (vpodzime) -- Add tests for zram_create_devices and zram_destroy_devices functions (vpodzime) -- Add the zram_destroy_devices function to the KBD plugin (vpodzime) -- Add first function to the KBD plugin: zram_create_devices (vpodzime) -- Add the KernelBlockDevices plugin (vpodzime) - -* Wed May 13 2015 Vratislav Podzimek - 0.13-1 -- Prevent a leaky test from running in Jenkins (vpodzime) -- Try harder when cleaning up after MD RAID tests (vpodzime) -- Improve the MD RAID activate/deactivate test (vpodzime) -- One more @contextmanager that needs try-finally (vpodzime) -- Do not require metadata version to be reported by 'mdadm --examine' (#1217900) (vpodzime) -- Make sure we always set things back in context managers (vpodzime) -- Make the release date for version 1.0 more realistic (vpodzime) -- Merge pull request #11 from vpodzime/master (vpodzime) -- Run utilities with LC_ALL=C (vpodzime) (#1219033) -- Free GMatchInfo instance even in case of no match (vpodzime) -- Resolve /dev/md/ symlinks when checking swap status. (dlehman) - -* Fri Apr 24 2015 Vratislav Podzimek - 0.12-1 -- Require minimum version of libblockdev-utils in some plugins (vpodzime) -- Report both stdout and stderr if exit code != 0 (vpodzime) - -* Fri Apr 17 2015 Vratislav Podzimek - 0.11-1 -- Fix issues with using overriden functions over ErrorProxy (vpodzime) -- Update the roadmap.rst and features.rst with new stuff (vpodzime) -- Fix two minor issues with docs generation (vpodzime) - -* Thu Apr 16 2015 Vratislav Podzimek - 0.10-1 -- Fix return type of the unload_plugins() function (vpodzime) -- Close the DL handle when check() or init() fail (vpodzime) -- Add one more check to the reload test (vpodzime) -- Drop reference to check() and init() functions (vpodzime) -- Add more cats to tests (vpodzime) -- Make regexp for getting btrfs version more generic (vpodzime) -- Merge pull request #8 from vpodzime/master-check_functions (vpodzime) -- Fix parameters passed to unoverridden swapon function (vpodzime) -- Implement and test swap plugin's check function (vpodzime) -- Implement and test MD RAID plugin's check function (vpodzime) -- Implement and test mpath plugin's check function (vpodzime) -- Try harder to get util's version (vpodzime) -- Implement and test loop plugin's check function (vpodzime) -- Implement and test DM plugin's check function (vpodzime) -- Implement and test BTRFS plugin's check function (vpodzime) -- Implement and test LVM plugin's check function (vpodzime) -- Init logging before loading plugins (vpodzime) -- Add function for utility availability checking (vpodzime) -- Fix default value for the fake_utils' path argument (vpodzime) -- Add ErrorProxy instance for the utils functions (vpodzime) -- Add function for version comparison (vpodzime) -- Merge pull request #9 from clumens/master (vpodzime) -- Disable pylint checking on the new exception proxy. (clumens) -- Fix XRules application and add a test for it (vpodzime) -- Raise NotImplementedError when an unavailable function is called (vpodzime) -- Merge pull request #4 from vpodzime/master-error_proxy (vpodzime) -- Merge branch 'master' into master-error_proxy (vpodzime) -- Merge pull request #5 from vpodzime/master-not_implemented_error (vpodzime) -- Add a simple test for unloaded/unavailable functions (vpodzime) -- Unload the plugins properly when reinit() is called (vpodzime) -- Raise error/exception when an unimplemented function is called (#1201475) (vpodzime) -- Do an ugly but necessary hack to make local GI overrides work (vpodzime) -- Add the __dir__ method to ErrorProxy (vpodzime) -- Add a rationale for the ErrorProxy to the overrides' docstring (vpodzime) -- Add some basic info about GI overrides to the documentation (vpodzime) -- Use pylint to check for errors in python overrides (vpodzime) -- Add the first small test for the ErrorProxy (vpodzime) -- Put the GI overrides in a special dir so that they are preferred (vpodzime) -- Add a cache for attributes already resolved by ErrorProxy (vpodzime) -- Implement the ErrorProxy python class and use it (vpodzime) - -* Tue Apr 07 2015 Vratislav Podzimek - 0.9-1 -- Merge pull request #7 from vpodzime/master-fw_raid_fixes (vpodzime) -- Try a bit harder when trying to determine MD RAID name (#1207317) (vpodzime) -- Don't be naïve about mdadm --detail telling us what we want (#1207317) (vpodzime) -- Ignore libblockdev tarballs (vpodzime) -- Implement a test of btrfs_list_subvolumes on data from bug report (vpodzime) -- Implement a context manager for running tests with fake utils (vpodzime) -- Do not try to cannonicalize MD UUIDs if we didn't get them (#1207317) (vpodzime) -- Fix the table in roadmap.rst (vpodzime) -- Enrich the roadmap.rst file and add info about new plans (vpodzime) -- Sync spec file with downstream (vpodzime) - -* Fri Mar 27 2015 Vratislav Podzimek - 0.8-1 -- Merge pull request #6 from vpodzime/master-sort_btrfs_subvolumes (vpodzime) -- Don't be naïve about mdadm providing us data we would like (#1206394) (vpodzime) -- Sort BTRFS subvolumes in a way that child never appears before parent (#1201120) (vpodzime) -- Let libcryptsetup handle LUKSname->/dev/mapper/LUKSname for us (vpodzime) -- Fix the crypto_luks_resize and create a test for it (vpodzime) -- Add targets to create the SRPM and RPM files easily (vpodzime) -- Don't round up to multiple of PE size bigger than max value of the rtype (vpodzime) -- Mark majority of MD RAID tests as slow (vpodzime) -- Merge pull request #1 from dashea/file-paths (vpodzime) -- Don't report error for no loop device associated with given file (vpodzime) -- Skip the detail_data.clean check when running tests in Jenkins (vpodzime) -- Make package file paths more specific (dshea) -- Implement and use MD RAID-specific wait for tests (vpodzime) -- Try to give MD RAID time to sync things before querying them (vpodzime) -- Fix the default value of the BDMDDetailData.clean field (vpodzime) -- Do cleanup after every single MD RAID tests (vpodzime) -- Do cleanup after every single LVM test (vpodzime) -- Do cleanup after every single BTRFS test (vpodzime) -- Make sure the LUKS device is closed and removed after tests (vpodzime) -- Make sure DM maps from tests are removed after tests (vpodzime) -- Make sure that loop devices are deactivated after tests (vpodzime) -- Make the tearDown method of the mpath test case better visible (vpodzime) -- Make sure that the swap is deactivated after tests (vpodzime) -- Fix docstrings in tests' utils helper functions (vpodzime) -- Improve the logging tests in utils_test.py (vpodzime) -- Update the features.rst file (vpodzime) -- Update the roadmap (vpodzime) -- Don't check if we get a mountpoint for BTRFS operations (vpodzime) - -* Sun Mar 22 2015 Peter Robinson 0.7-2 -- Ship license as per packaging guidelines -- plugins-all should depend on base library too -- Add dev docs - -* Fri Feb 27 2015 Vratislav Podzimek - 0.7-1 -- Be ready for mdadm --examine to not provide some of the values we want (vpodzime) -- Add exit code information to exec logging (vpodzime) -- Improve and add tests (vpodzime) -- Mark the test_force_plugin and test_reload as slow (vpodzime) -- Make sure we get some devices when creating btrfs volume (vpodzime) -- Add override for the lvremove function (vpodzime) -- Do not create LUKS format with no passphrase and no key file (vpodzime) -- Make sure we use the /dev/mapper/... path for luks_status (vpodzime) - -* Thu Feb 19 2015 Vratislav Podzimek - 0.6-1 -- Don't report error when non-existing swap's status is queried (vpodzime) -- Make libblockdev-plugins-all pull the same version of plugins (vpodzime) -- Don't report error when asked for a backing file of an uknown loop (vpodzime) -- Fix accidental change in the spec's changelog (vpodzime) - -* Mon Feb 16 2015 Vratislav Podzimek - 0.5-1 -- Add tests for what we can easily test from the mpath plugin (vpodzime) -- Add link to sources to the documentation (vpodzime) -- Add missing symbols into the libblockdev-sections.txt file (vpodzime) -- Do not build docs for testing (vpodzime) -- Add the bd_try_init function (vpodzime) -- Log stdout and stderr output when running processes (vpodzime) -- Allow a subset of plugins to be load instead of all (vpodzime) -- Make sure devmapper doesn't spam stdout with tons of messages (vpodzime) -- Let debug messages go to stderr when running ipython (vpodzime) -- Give plugins a way to initialize themselves (vpodzime) -- Give plugins a way how to check if they could run properly (vpodzime) -- Allow a subset of plugins to be load instead of all [TEST NEEDED] (vpodzime) -- Make sure we use the whole /dev/mapper path for cryptsetup (vpodzime) -- Fix vg_pv_count parsing when getting info about PV (vpodzime) -- Set default values to data structures if real values are not available (vpodzime) -- Fix the parameter name specifying pool metadata size (vpodzime) -- Activate LUKS as ReadWrite in luks_open (vpodzime) -- Make sure we pass key_size to cryptsetup in bytes (vpodzime) -- Add the min_entropy parameter to luks_format Python overrides (vpodzime) -- Pass size in KiB instead of B to lvcreate (vpodzime) -- Add underscore into dataalignment and metadatasize parameter names (vpodzime) -- Don't report error if non-mpath device is tested for being mpath member (vpodzime) -- Fix name of the invoked utility in mpath_set_friendly_names (vpodzime) - -* Sat Jan 31 2015 Vratislav Podzimek - 0.4-1 -- Improve the test for lvm_set_global_config (vpodzime) -- Fix some minor issues in the spec file (vpodzime) -- Fix issues with the LVM global config str (vpodzime) -- Add couple more Python overrides (vpodzime) -- Fix the name of the lvm_thlvpoolname() function in the header file (vpodzime) -- Use assertEqual instead of assertTrue(a == b) (vpodzime) -- Add the min_entropy parameter to luks_format (vpodzime) -- Move internal dmraid-related macros into the source file (vpodzime) -- Add an override for the md_add function (vpodzime) -- Fix parameters in luks_open python overrides (vpodzime) -- Prevent init() from being done multiple times and provide a test function (vpodzime) -- Add the roadmap.rst document (vpodzime) -- Remove an extra parenthesis in one of the docstrings (vpodzime) -- Move the mddetail function next to the mdexamine function (vpodzime) -- Add some more constants required by blivet (vpodzime) - -* Wed Jan 21 2015 Vratislav Podzimek - 0.3-1 -- Require volume_key-devel in a version that fixes build issues (vpodzime) -- Fix Python 2 devel package name in BuildRequires (vpodzime) -- Generate docs for the library and all plugins (vpodzime) -- Make doc comments better for documentation generation (vpodzime) -- Fix parameter names in function prototypes (vpodzime) -- Add the metadatasize parameter to pvcreate (vpodzime) -- Add the dataalignment parameter to lvm_pvcreate (vpodzime) -- Export non-internal constants via introspection (vpodzime) -- Expand size constants in the GI-scanned files (vpodzime) -- Fix usage printing in the boilerplate_generator (vpodzime) -- Add the build directory to .gitignore (vpodzime) -- Add the md_run function (vpodzime) -- Fix some issues in Python overrides (vpodzime) -- Add the escrow_device function to the crypto plugin (vpodzime) -- Fix version of GI files in the Makefile (vpodzime) -- Make the order of release target's dependencies more explicit (vpodzime) - -* Mon Jan 12 2015 Vratislav Podzimek - 0.2-1 -- Fix dependencies of the release target (vpodzime) -- Python overrides for the GI-generated bindings (vpodzime) -- Pass version info to the code and use it to load plugins (vpodzime) - -* Wed Dec 10 2014 Vratislav Podzimek - 0.1-1 -- Initial release diff --git a/libblockdev.spec b/libblockdev.spec new file mode 100644 index 0000000..3da502f --- /dev/null +++ b/libblockdev.spec @@ -0,0 +1,1083 @@ +%define with_python3 1 +%define with_gtk_doc 1 +%define with_btrfs 1 +%define with_crypto 1 +%define with_dm 1 +%define with_loop 1 +%define with_lvm 1 +%define with_lvm_dbus 1 +%define with_mdraid 1 +%define with_mpath 1 +%define with_swap 1 +%define with_part 1 +%define with_fs 1 +%define with_nvdimm 1 +%define with_gi 1 +%define with_escrow 1 +%define with_tools 1 +%define with_nvme 1 +%define with_smart 1 +%define with_smartmontools 1 + +# btrfs is not available on RHEL > 7 +%if 0%{?rhel} > 7 || %{with_btrfs} == 0 +%define with_btrfs 0 +%define btrfs_copts --without-btrfs +%endif + +%if %{with_btrfs} != 1 +%define btrfs_copts --without-btrfs +%endif +%if %{with_crypto} != 1 +%define crypto_copts --without-crypto +%else +%if %{with_escrow} != 1 +%define crypto_copts --without-escrow +%endif +%endif +%if %{with_dm} != 1 +%define dm_copts --without-dm +%else +%endif +%if %{with_loop} != 1 +%define loop_copts --without-loop +%endif +%if %{with_lvm} != 1 +%define lvm_copts --without-lvm +%endif +%if %{with_lvm_dbus} != 1 +%define lvm_dbus_copts --without-lvm_dbus +%endif +%if %{with_mdraid} != 1 +%define mdraid_copts --without-mdraid +%endif +%if %{with_mpath} != 1 +%define mpath_copts --without-mpath +%endif +%if %{with_swap} != 1 +%define swap_copts --without-swap +%endif +%if %{with_part} != 1 +%define part_copts --without-part +%endif +%if %{with_fs} != 1 +%define fs_copts --without-fs +%endif +%if %{with_nvdimm} != 1 +%define nvdimm_copts --without-nvdimm +%endif +%if %{with_tools} != 1 +%define tools_copts --without-tools +%endif +%if %{with_gi} != 1 +%define gi_copts --disable-introspection +%endif +%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} %{?smart_copts} %{?smartmontools_copts} + +Name: libblockdev +Version: 3.1.0 +Release: 7%{?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 + +BuildRequires: make +BuildRequires: glib2-devel +%if %{with_gi} +BuildRequires: gobject-introspection-devel +%endif +%if %{with_python3} +BuildRequires: python3-devel +%endif +%if %{with_gtk_doc} +BuildRequires: gtk-doc +%endif +BuildRequires: glib2-doc +BuildRequires: autoconf-archive + +# obsolete removed subpackages to allow upgrades +Provides: libblockdev-kbd = %{version}-%{release} +Obsoletes: libblockdev-kbd < %{version}-%{release} +Provides: libblockdev-vdo = %{version}-%{release} +Obsoletes: libblockdev-vdo < %{version}-%{release} + +Requires: %{name}-utils%{?_isa} = %{version}-%{release} + +%description +The libblockdev is a C library with GObject introspection support that can be +used for doing low-level operations with block devices like setting up LVM, +BTRFS, LUKS or MD RAID. The library uses plugins (LVM, BTRFS,...) and serves as +a thin wrapper around its plugins' functionality. All the plugins, however, can +be used as standalone libraries. One of the core principles of libblockdev is +that it is stateless from the storage configuration's perspective (e.g. it has +no information about VGs when creating an LV). + +%package devel +Summary: Development files for libblockdev +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +# obsolete removed devel subpackages to allow upgrades +Provides: libblockdev-kbd-devel = %{version}-%{release} +Obsoletes: libblockdev-kbd-devel < %{version}-%{release} +Provides: libblockdev-vdo-devel = %{version}-%{release} +Obsoletes: libblockdev-vdo-devel < %{version}-%{release} + +%description devel +This package contains header files and pkg-config files needed for development +with the libblockdev library. + +%if %{with_python3} +%package -n python3-blockdev +Summary: Python3 gobject-introspection bindings for libblockdev +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: python3-gobject-base +Requires: python3-bytesize +%{?python_provide:%python_provide python3-blockdev} + +%description -n python3-blockdev +This package contains enhancements to the gobject-introspection bindings for +libblockdev in Python3. +%endif + +%package utils +BuildRequires: kmod-devel +Summary: A library with utility functions for the libblockdev library + +%description utils +The libblockdev-utils is a library providing utility functions used by the +libblockdev library and its plugins. + +%package utils-devel +Summary: Development files for libblockdev-utils +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description utils-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-utils library. + + +%if %{with_btrfs} +%package btrfs +BuildRequires: libbytesize-devel +Summary: The BTRFS plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: btrfs-progs + +%description btrfs +The libblockdev library plugin (and in the same time a standalone library) +providing the BTRFS-related functionality. + +%package btrfs-devel +Summary: Development files for the libblockdev-btrfs plugin/library +Requires: %{name}-btrfs%{?_isa} = %{version}-%{release} +Requires: glib2-devel +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} + +%description btrfs-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-btrfs plugin/library. +%endif + + +%if %{with_crypto} +%package crypto +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +BuildRequires: cryptsetup-devel >= 2.3.0 +BuildRequires: libblkid-devel +BuildRequires: keyutils-libs-devel + +%if %{with_escrow} +BuildRequires: volume_key-devel >= 0.3.9-7 +BuildRequires: nss-devel +%endif + +Summary: The crypto plugin for the libblockdev library + +%description crypto +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to encrypted devices (LUKS). + +%package crypto-devel +Summary: Development files for the libblockdev-crypto plugin/library +Requires: %{name}-crypto%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description crypto-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-crypto plugin/library. +%endif + + +%if %{with_dm} +%package dm +BuildRequires: device-mapper-devel +BuildRequires: systemd-devel +Summary: The Device Mapper plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: device-mapper + +%description dm +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to Device Mapper. + +%package dm-devel +Summary: Development files for the libblockdev-dm plugin/library +Requires: %{name}-dm%{?_isa} = %{version}-%{release} +Requires: glib2-devel +Requires: device-mapper-devel +Requires: systemd-devel +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} + +%description dm-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-dm plugin/library. +%endif + + +%if %{with_fs} +%package fs +BuildRequires: libblkid-devel +BuildRequires: libmount-devel +BuildRequires: libuuid-devel +BuildRequires: e2fsprogs-devel +Summary: The FS plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} + +%description fs +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to operations with file systems. + +%package fs-devel +Summary: Development files for the libblockdev-fs plugin/library +Requires: %{name}-fs%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description fs-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-fs plugin/library. +%endif + + +%if %{with_loop} +%package loop +Summary: The loop plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} + +%description loop +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to loop devices. + +%package loop-devel +Summary: Development files for the libblockdev-loop plugin/library +Requires: %{name}-loop%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description loop-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-loop plugin/library. +%endif + + +%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 + +%description lvm +The libblockdev library plugin (and in the same time a standalone library) +providing the LVM-related functionality. + +%package lvm-devel +Summary: Development files for the libblockdev-lvm plugin/library +Requires: %{name}-lvm%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description lvm-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-lvm plugin/library. +%endif + +%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 + +%description lvm-dbus +The libblockdev library plugin (and in the same time a standalone library) +providing the LVM-related functionality utilizing the LVM DBus API. + +%package lvm-dbus-devel +Summary: Development files for the libblockdev-lvm-dbus plugin/library +Requires: %{name}-lvm-dbus%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description lvm-dbus-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-lvm-dbus plugin/library. +%endif + + +%if %{with_mdraid} +%package mdraid +BuildRequires: libbytesize-devel +Summary: The MD RAID plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: mdadm + +%description mdraid +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to MD RAID. + +%package mdraid-devel +Summary: Development files for the libblockdev-mdraid plugin/library +Requires: %{name}-mdraid%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description mdraid-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-mdraid plugin/library. +%endif + + +%if %{with_mpath} +%package mpath +BuildRequires: device-mapper-devel +Summary: The multipath plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Recommends: device-mapper-multipath + +%description mpath +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to multipath devices. + +%package mpath-devel +Summary: Development files for the libblockdev-mpath plugin/library +Requires: %{name}-mpath%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description mpath-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-mpath plugin/library. +%endif + +%if %{with_nvdimm} +%package nvdimm +BuildRequires: ndctl-devel +BuildRequires: libuuid-devel +Summary: The NVDIMM plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: ndctl + +%description nvdimm +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to operations with NVDIMM devices. + +%package nvdimm-devel +Summary: Development files for the libblockdev-nvdimm plugin/library +Requires: %{name}-nvdimm%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description nvdimm-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-nvdimm plugin/library. +%endif + + +%if %{with_nvme} +%package nvme +BuildRequires: libnvme-devel +BuildRequires: libuuid-devel +Summary: The NVMe plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} + +%description nvme +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to operations with NVMe devices. + +%package nvme-devel +Summary: Development files for the libblockdev-nvme plugin/library +Requires: %{name}-nvme%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description nvme-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-nvme plugin/library. +%endif + + +%if %{with_part} +%package part +BuildRequires: libfdisk-devel +Summary: The partitioning plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: util-linux + +%description part +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to partitioning. + +%package part-devel +Summary: Development files for the libblockdev-part plugin/library +Requires: %{name}-part%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description part-devel +This package contains header files and pkg-config files needed for development +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 +Summary: The swap plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: util-linux + +%description swap +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to swap devices. + +%package swap-devel +Summary: Development files for the libblockdev-swap plugin/library +Requires: %{name}-swap%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description swap-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-swap plugin/library. +%endif + +%if %{with_tools} +%package tools +Summary: Various nice tools based on libblockdev +Requires: %{name} = %{version}-%{release} +Requires: %{name}-lvm = %{version}-%{release} +BuildRequires: libbytesize-devel +BuildRequires: parted-devel +%if %{with_lvm_dbus} == 1 +Recommends: %{name}-lvm-dbus +%endif + +%description tools +Various nice storage-related tools based on libblockdev. + +%endif + +%ifarch s390 s390x +%package s390 +Summary: The s390 plugin for the libblockdev library +Requires: %{name}-utils%{?_isa} = %{version}-%{release} +Requires: s390utils + +%description s390 +The libblockdev library plugin (and in the same time a standalone library) +providing the functionality related to s390 devices. + +%package s390-devel +Summary: Development files for the libblockdev-s390 plugin/library +Requires: %{name}-s390%{?_isa} = %{version}-%{release} +Requires: %{name}-utils-devel%{?_isa} = %{version}-%{release} +Requires: glib2-devel + +%description s390-devel +This package contains header files and pkg-config files needed for development +with the libblockdev-s390 plugin/library. +%endif + +%package plugins-all +Summary: Meta-package that pulls all the libblockdev plugins as dependencies +Requires: %{name}%{?_isa} = %{version}-%{release} + +%if %{with_btrfs} +Requires: %{name}-btrfs%{?_isa} = %{version}-%{release} +%endif + +%if %{with_crypto} +Requires: %{name}-crypto%{?_isa} = %{version}-%{release} +%endif + +%if %{with_dm} +Requires: %{name}-dm%{?_isa} = %{version}-%{release} +%endif + +%if %{with_fs} +Requires: %{name}-fs%{?_isa} = %{version}-%{release} +%endif + +%if %{with_loop} +Requires: %{name}-loop%{?_isa} = %{version}-%{release} +%endif + +%if %{with_lvm} +Requires: %{name}-lvm%{?_isa} = %{version}-%{release} +%endif + +%if %{with_mdraid} +Requires: %{name}-mdraid%{?_isa} = %{version}-%{release} +%endif + +%if %{with_mpath} +Requires: %{name}-mpath%{?_isa} = %{version}-%{release} +%endif + +%if %{with_nvdimm} +Requires: %{name}-nvdimm%{?_isa} = %{version}-%{release} +%endif + +%if %{with_nvme} +Requires: %{name}-nvme%{?_isa} = %{version}-%{release} +%endif + +%if %{with_part} +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 + +%ifarch s390 s390x +Requires: %{name}-s390%{?_isa} = %{version}-%{release} +%endif + +%description plugins-all +A meta-package that pulls all the libblockdev plugins as dependencies. + + +%prep +%autosetup -n %{name}-%{version} -N -b1 +%autosetup -n %{name}-%{version} -p1 + +%build +autoreconf -ivf +%configure %{?configure_opts} +%{__make} %{?_smp_mflags} + +%install +%{make_install} +find %{buildroot} -type f -name "*.la" | xargs %{__rm} + + +%ldconfig_scriptlets +%ldconfig_scriptlets utils + +%if %{with_btrfs} +%ldconfig_scriptlets btrfs +%endif + +%if %{with_crypto} +%ldconfig_scriptlets crypto +%endif + +%if %{with_dm} +%ldconfig_scriptlets dm +%endif + +%if %{with_fs} +%ldconfig_scriptlets fs +%endif + +%if %{with_loop} +%ldconfig_scriptlets loop +%endif + +%if %{with_lvm} +%ldconfig_scriptlets lvm +%endif + +%if %{with_lvm_dbus} +%ldconfig_scriptlets lvm-dbus +%endif + +%if %{with_mdraid} +%ldconfig_scriptlets mdraid +%endif + +%if %{with_mpath} +%ldconfig_scriptlets mpath +%endif + +%if %{with_nvdimm} +%ldconfig_scriptlets nvdimm +%endif + +%if %{with_nvme} +%ldconfig_scriptlets nvme +%endif + +%if %{with_part} +%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 + +%ifarch s390 s390x +%ldconfig_scriptlets s390 +%endif + + +%files +%{!?_licensedir:%global license %%doc} +%license LICENSE +%{_libdir}/libblockdev.so.* +%if %{with_gi} +%{_libdir}/girepository*/BlockDev*.typelib +%endif +%dir %{_sysconfdir}/libblockdev +%dir %{_sysconfdir}/libblockdev/3/conf.d +%config %{_sysconfdir}/libblockdev/3/conf.d/00-default.cfg + +%files devel +%{_libdir}/libblockdev.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/blockdev.h +%{_includedir}/blockdev/plugins.h +%{_libdir}/pkgconfig/blockdev.pc +%if %{with_gtk_doc} +%{_datadir}/gtk-doc/html/libblockdev +%endif +%if %{with_gi} +%{_datadir}/gir*/BlockDev*.gir +%endif + +%if %{with_python3} +%files -n python3-blockdev +%{python3_sitearch}/gi/overrides/BlockDev* +%{python3_sitearch}/gi/overrides/__pycache__/BlockDev* +%endif + +%files utils +%{_libdir}/libbd_utils.so.* + +%files utils-devel +%{_libdir}/libbd_utils.so +%{_libdir}/pkgconfig/blockdev-utils.pc +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/utils.h +%{_includedir}/blockdev/sizes.h +%{_includedir}/blockdev/exec.h +%{_includedir}/blockdev/extra_arg.h +%{_includedir}/blockdev/dev_utils.h +%{_includedir}/blockdev/module.h +%{_includedir}/blockdev/dbus.h +%{_includedir}/blockdev/logging.h + + +%if %{with_btrfs} +%files btrfs +%{_libdir}/libbd_btrfs.so.* + +%files btrfs-devel +%{_libdir}/libbd_btrfs.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/btrfs.h +%endif + + +%if %{with_crypto} +%files crypto +%{_libdir}/libbd_crypto.so.* + +%files crypto-devel +%{_libdir}/libbd_crypto.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/crypto.h +%endif + + +%if %{with_dm} +%files dm +%{_libdir}/libbd_dm.so.* + +%files dm-devel +%{_libdir}/libbd_dm.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/dm.h +%endif + + +%if %{with_fs} +%files fs +%{_libdir}/libbd_fs.so.* + +%files fs-devel +%{_libdir}/libbd_fs.so +%dir %{_includedir}/blockdev +%dir %{_includedir}/blockdev/fs +%{_includedir}/blockdev/fs.h +%{_includedir}/blockdev/fs/*.h +%endif + + +%if %{with_loop} +%files loop +%{_libdir}/libbd_loop.so.* + +%files loop-devel +%{_libdir}/libbd_loop.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/loop.h +%endif + + +%if %{with_lvm} +%files lvm +%{_libdir}/libbd_lvm.so.* + +%files lvm-devel +%{_libdir}/libbd_lvm.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/lvm.h +%endif + + +%if %{with_lvm_dbus} +%files lvm-dbus +%{_libdir}/libbd_lvm-dbus.so.* +%config %{_sysconfdir}/libblockdev/3/conf.d/10-lvm-dbus.cfg + +%files lvm-dbus-devel +%{_libdir}/libbd_lvm-dbus.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/lvm.h +%endif + + +%if %{with_mdraid} +%files mdraid +%{_libdir}/libbd_mdraid.so.* + +%files mdraid-devel +%{_libdir}/libbd_mdraid.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/mdraid.h +%endif + + +%if %{with_mpath} +%files mpath +%{_libdir}/libbd_mpath.so.* + +%files mpath-devel +%{_libdir}/libbd_mpath.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/mpath.h +%endif + + +%if %{with_nvdimm} +%files nvdimm +%{_libdir}/libbd_nvdimm.so.* + +%files nvdimm-devel +%{_libdir}/libbd_nvdimm.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/nvdimm.h +%endif + + +%if %{with_nvme} +%files nvme +%{_libdir}/libbd_nvme.so.* + +%files nvme-devel +%{_libdir}/libbd_nvme.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/nvme.h +%endif + + +%if %{with_part} +%files part +%{_libdir}/libbd_part.so.* + +%files part-devel +%{_libdir}/libbd_part.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/part.h +%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.* + +%files swap-devel +%{_libdir}/libbd_swap.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/swap.h +%endif + + +%if %{with_tools} +%files tools +%{_bindir}/lvm-cache-stats +%{_bindir}/vfat-resize +%endif + +%ifarch s390 s390x +%files s390 +%{_libdir}/libbd_s390.so.* + +%files s390-devel +%{_libdir}/libbd_s390.so +%dir %{_includedir}/blockdev +%{_includedir}/blockdev/s390.h +%endif + +%files plugins-all + +%changelog +* Wed Jul 24 2024 Vojtech Trefny - 3.1.0-7 +- Backport libblockdev-smart plugin + Resolves: RHEL-49675 + +* Fri Jul 19 2024 Vojtech Trefny - 3.1.0-6 +- Fix passing size for pvresize over DBus + Resolves: RHEL-45872 +- Upstream kernel VDO support + Resolves: RHEL-31953 + +* Mon Jun 24 2024 Troy Dawson - 3.1.0-5 +- Bump release for June 2024 mass rebuild + +* Thu May 30 2024 Vojtech Trefny - 3.1.0-4 +- Check also for aliases in bd_utils_have_kernel_module + Resolves: RHEL-37249 + +* Tue May 14 2024 Vojtech Trefny - 3.1.0-3 +- Fix creating MD RAID arrays without specifying bitmap localtion + Resolves: RHEL-31379 +- crypto: Fix double free in bd_crypto_luks_remove_key + Resolves: RHEL-31489 +- tests: Fix running tests without ntfsprogs + Resolves: RHEL-32875 + +* Thu Jan 25 2024 Fedora Release Engineering - 3.1.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Fri Jan 19 2024 Vojtech Trefny - 3.1.0-1 +- tests: Skip some checks for btrfs errors with btrfs-progs 6.6.3 (vtrefny) +- Fix missing progress initialization in bd_crypto_luks_add_key (vtrefny) +- fs: Report reason for open() and ioctl() failures (tbzatek) +- utils: Add expected printf string annotation (tbzatek) +- lvm-dbus: Avoid using already-freed memory (tbzatek) +- lvm-dbus: Fix leaking error (tbzatek) +- python: Add a deepcopy function to our structs (vtrefny) +- tests: Remove unreliable nvme attribute checks (tbzatek) +- tests: Use BDPluginSpec constructor in LVM DBus plugin tests (vtrefny) +- tests: Avoid setting up intermediary loop device for the nvme target (tbzatek) +- tests: Default to /tmp for create_sparse_tempfile() (tbzatek) +- part: Fix potential double free when getting parttype (vtrefny) +- Mark NVDIMM plugin as deprecated since 3.1 (vtrefny) +- tests: Remove some obsolete rules to skip tests (vtrefny) +- fs: Add support for getting filesystem min size for NTFS and Ext (vtrefny) +- fs: Fix allowed UUID for generic mkfs with VFAT (vtrefny) +- fs: Add a generic function to check for fs info availability (vtrefny) +- fs: Add a function to check label format for F2FS (vtrefny) +- swap: Add support for checking label and UUID format (vtrefny) +- ci: Remove the custom version command for Packit (vtrefny) +- ci: Manually prepare spec file for Packit (vtrefny) +- overrides: Remove unused 'sys' import (vtrefny) +- Add BDPluginSpec constructor and use it in plugin_specs_from_names (vtrefny) +- Sync spec with downstream (vtrefny) +- ci: Add an action to compile libblockdev with different compilers (vtrefny) + +* Fri Oct 13 2023 Vojtech Trefny - 3.0.4-1 +- tests: Fix "invalid escape sequence '\#'" warning from Python 3.12 (vtrefny) +- tests: Fail early when recompilation fails in library_test (vtrefny) +- lvm-dbus: Replace g_critical calls with bd_utils_log_format (vtrefny) +- Use log function when calling a plugin function that is not loaded (vtrefny) +- logging: Default to DEBUG log level if compiled with --enable-debug (vtrefny) +- nvme: Rework memory allocation for device ioctls (tbzatek) +- packit: Add configuration for downstream builds (vtrefny) +- fs: correct btrfs set label description (jvanderwaa) +- fs: Disable progress for ntfsresize (vtrefny) +- part: Do not open disk read-write for read only operations (vtrefny) +- ci: Bump actions/checkout from v3 to v4 (vtrefny) +- plugins: btrfs: use g_autofree where possible for g_free (jvanderwaa) +- plugins: use g_autofree for free'ing g_char's (jvanderwaa) +- spec: Move obsoleted devel subpackages to libblockdev-devel (vtrefny) +- spec: Obsolete vdo plugin packages (vtrefny) + +* Wed Sep 06 2023 Vojtech Trefny - 3.0.3-2 +- Obsolete vdo plugin packages (#2237477) + +* Thu Aug 31 2023 Vojtech Trefny - 3.0.3-1 +- crypto: Correctly convert passphrases from Python to C (vtrefny) +- tests: Minor NVMe HostNQN fixes (tbzatek) +- nvme: Generate HostID when missing (tbzatek) +- Always use "--fs ignore" with lvresize (mvollmer) +- nvme: Use interim buffer for nvme_get_log_sanitize() (tbzatek) +- fs: Fix memory leak (vtrefny) +- fs: Fix leaking directories with temporary mounts (vtrefny) +- tests: Specificy required versions when importing GLib and BlockDev introspection (biebl) +- tests: Replace deprecated unittest assert calls (vtrefny) + +* Thu Jul 20 2023 Vojtech Trefny - 3.0.2-1 +- fs: Avoid excess logging in extract_e2fsck_progress (vtrefny) +- loop: Report BD_LOOP_ERROR_DEVICE on empty loop devices (tbzatek) +- lib: Silence the missing DEFAULT_CONF_DIR_PATH (tbzatek) +- fs: Document that generic functions can mount filesystems (vtrefny) +- fs: Use read-only mount where possible for generic FS functions (vtrefny) +- docs: Fix test quotation (marecki) +- fs: Fix unused error in extract_e2fsck_progress (vtrefny) +- Use ntfsinfo instead of ntfscluster for faster bd_fs_ntfs_get_info (amubtdx) +- Restrict list of exported symbols via -export-symbols-regex (biebl) +- Fix formatting in NEWS.rst (vtrefny) + +* Tue Jul 04 2023 Python Maint - 3.0.1-2 +- Rebuilt for Python 3.12 + +* Tue Jul 04 2023 Vojtech Trefny - 3.0.1-1 +- fs: Simplify struct BDFSInfo (tbzatek) +- boilerplate_generator: Annotate stub func args as G_GNUC_UNUSED (tbzatek) +- crypto: Remove stray struct redefinition (tbzatek) +- loop: Remove unused variable (tbzatek) +- build: Exit before AC_OUTPUT on error (tbzatek) +- loop: define LOOP_SET_BLOCK_SIZE is not defined (giulio.benetti) +- Make the conf.d directory versioned (vtrefny) +- configure: Fix MAJOR_VER macro (vtrefny) +- spec: Add dependency on libblockdev-utils to the s390 plugin (vtrefny) +- nvme: Mark private symbols as hidden (tbzatek) +- dist: Sync spec with downstream (vtrefny) +- misc: Update steps and Dockerfile for Python documentation (vtrefny) +- fs: Add missing copy and free functions to the header file (vtrefny) +- lvm: Add bd_lvm_segdata_copy/free to the header file (vtrefny) +- loop: Remove bd_loop_get_autoclear definition (vtrefny) +- lvm: Fix declaration for bd_lvm_vdolvpoolname (vtrefny) +- lvm: Make _vglock_start_stop static (vtrefny) +- vdo_stats: Remove unused libparted include (vtrefny) + +* Thu Jun 29 2023 Python Maint - 3.0-2 +- Rebuilt for Python 3.12 + +* Fri Jun 23 2023 Vojtech Trefny - 3.0-1 +- Libblockdev 3.0 release diff --git a/sources b/sources new file mode 100644 index 0000000..3c4cbda --- /dev/null +++ b/sources @@ -0,0 +1,2 @@ +SHA512 (libblockdev-3.1.0.tar.gz) = 1e5f8155ceaf1b1f02607d0e2075bfe3b1a2c8d6845de5c90201e6b7aefde3bab7af8e9abf137763e6e9ca42fefe5218697ea63731b3b4952eb004b01cf3719a +SHA512 (smart-tests.tar.gz) = 6ece82eef09c3b22c8f1473653d791bcac7316c3f267368170cea5b152d55603d2a1014c729c4ed190e39660a8734a51989eb53c4a427b843e074f750b1f9a9b