LCOV - code coverage report
Current view: top level - plugins/smart - smart-common.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 7.7 % 52 4
Test Date: 2026-01-26 13:19:28 Functions: 22.2 % 9 2
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2014-2024 Red Hat, Inc.
       3              :  *
       4              :  * This library is free software; you can redistribute it and/or
       5              :  * modify it under the terms of the GNU Lesser General Public
       6              :  * License as published by the Free Software Foundation; either
       7              :  * version 2.1 of the License, or (at your option) any later version.
       8              :  *
       9              :  * This library is distributed in the hope that it will be useful,
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12              :  * Lesser General Public License for more details.
      13              :  *
      14              :  * You should have received a copy of the GNU Lesser General Public
      15              :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      16              :  *
      17              :  * Author: Tomas Bzatek <tbzatek@redhat.com>
      18              :  */
      19              : 
      20              : #include <glib.h>
      21              : #include <string.h>
      22              : #include <stdio.h>
      23              : #include <unistd.h>
      24              : #include <errno.h>
      25              : 
      26              : #include <blockdev/utils.h>
      27              : #include <check_deps.h>
      28              : 
      29              : #include "smart.h"
      30              : #include "smart-private.h"
      31              : 
      32              : /**
      33              :  * SECTION: smart
      34              :  * @short_description: S.M.A.R.T. device reporting and management.
      35              :  * @title: SMART
      36              :  * @include: smart.h
      37              :  *
      38              :  * Plugin for ATA and SCSI/SAS S.M.A.R.T. device reporting and management. For NVMe
      39              :  * health reporting please use the native [`nvme`][libblockdev-NVMe] plugin.
      40              :  *
      41              :  * This libblockdev plugin strives to provide good enough abstraction on top of vastly
      42              :  * different backend implementations. Two plugin implementations are available:
      43              :  * `libatasmart` (default) and `smartmontools` (experimental).
      44              :  *
      45              :  * Not all plugin implementations provide full functionality and it is advised
      46              :  * to use standard libblockdev tech query functions for feature availability testing.
      47              :  * For example, the `libatasmart` plugin only provides ATA functionality and error
      48              :  * is returned when any SCSI function is called.
      49              :  *
      50              :  * ## libatasmart plugin #
      51              :  *
      52              :  * An implementation proven for over a decade, being essentially a port of UDisks code.
      53              :  * The `libatasmart` library is reasonably lightweight with minimal dependencies,
      54              :  * light on I/O and consuming C API directly with clearly defined data types.
      55              :  * However essentially no quirks or any drive database is present in the library
      56              :  * (apart from a couple of very old laptop drives).
      57              :  *
      58              :  * ## smartmontools plugin #
      59              :  *
      60              :  * In contrast to libatasmart, the smartmontools project is a feature-rich
      61              :  * implementation supporting specialties like vendor-specific data blocks. It is
      62              :  * a considerably heavier implementation I/O-wise due to device type detection and
      63              :  * retrieval of more data blocks from the drive.
      64              :  *
      65              :  * There's no C API at the moment and the plugin resorts to executing the `smartctl`
      66              :  * command and parsing its JSON output, that is by nature loosely-defined. This
      67              :  * presents challenges in data type conversions, interpretation of printed values
      68              :  * and volatile JSON key presence. Besides, executing external commands always brings
      69              :  * certain performance overhead and caution is advised when retrieving SMART data
      70              :  * from multiple drives in parallel.
      71              :  *
      72              :  * ## Attribute naming and value interpretation #
      73              :  *
      74              :  * Check #BDSmartATAAttribute for the struct members overview first. The plugin
      75              :  * public API provides both the implementation-specific attribute names/values
      76              :  * as well as unified ('well-known', translated) interpretation that is preferred
      77              :  * for general use.
      78              :  *
      79              :  * The `well_known_name` property follows the libatasmart-style naming -
      80              :  * e.g. `'power-on-hours'`. Unknown or untrusted attributes are either provided
      81              :  * in the form of `'attribute-123'` or by %NULL.
      82              :  *
      83              :  * Similarly, value of an attribute is provided in variety of interpretations,
      84              :  * subject to availability:
      85              :  * - the `value`, `worst` and `threshold` are normalized values in typical S.M.A.R.T. fashion
      86              :  * - the `value_raw` as a 64-bit untranslated value with no further context
      87              :  *   of which bits are actually valid for a particular attribute
      88              :  * - the `pretty_value_string` as an implementation-specific string representation,
      89              :  *   intended for end-user display
      90              :  * - the `pretty_value` and `pretty_value_unit` as a libatasmart-style of unified value/type pair
      91              :  *
      92              :  * Both libblockdev plugins strive for best effort of providing accurate values,
      93              :  * however there are often challenges ranging from string-to-number conversion,
      94              :  * multiple values being unpacked from a single raw number or not having enough
      95              :  * context provided by the underlying library for a trusted value interpretation.
      96              :  *
      97              :  * ## Attribute validation #
      98              :  *
      99              :  * It may seem obvious to use numerical attribute ID as an authoritative attribute
     100              :  * identifier, however in reality drive vendors tend not to stick with public
     101              :  * specifications. Attributes are often reused for vendor-specific values and this
     102              :  * differs from model to model even for a single vendor. This is more often the case
     103              :  * with SSD drives than traditional HDDs.
     104              :  *
     105              :  * Historically it brought confusion and false alarms on user's end and eventually
     106              :  * led to some form of quirk database in most projects. Maintaining such database
     107              :  * is a lifetime task and the only successful effort is the smartmontools' `drivedb.h`
     108              :  * collection. Quirks are needed for about everything - meaning of a particular
     109              :  * attribute (i.e. a 'well-known' name), interpretation of a raw value, all this
     110              :  * filtered by drive model string and firmware revision.
     111              :  *
     112              :  * However even there not everything is consistent and slight variations in
     113              :  * a 'well-known' name can be found. Besides, the attribute naming syntax differs
     114              :  * from our chosen libatasmart-style form.
     115              :  *
     116              :  * For this reason an internal libblockdev translation table has been introduced
     117              :  * to ensure a bit of consistency. The translation table is kept conservative,
     118              :  * is by no means complete and may get extended in future libblockdev releases.
     119              :  * As a result, some attributes may be reported as 'untrusted' or 'unknown'.
     120              :  *
     121              :  * The translation table at this point doesn't handle 'moves' where a different
     122              :  * attribute ID has been assigned for otherwise well defined attribute.
     123              :  *
     124              :  * An experimental `drivedb.h` parser is provided for the libatasmart plugin
     125              :  * as an additional tier of validation based on actual drive model + firmware match.
     126              :  * Being a C header file, the `drivedb.h` definitions are compiled in the plugin.
     127              :  * There's no support for loading external `drivedb.h` file though. This however
     128              :  * only serves for validation. Providing backwards mapping to libatasmart-style
     129              :  * of attributes is considered as a TODO.
     130              :  *
     131              :  * ## Device type detection, multipath #
     132              :  *
     133              :  * There's a big difference in how a drive is accessed. While `libatasmart` performs
     134              :  * only very basic device type detection based on parent subsystem as retrieved from
     135              :  * the udev database, `smartctl` implements logic to determine which protocol to use,
     136              :  * supporting variety of passthrough mechanisms and interface bridges. Such detection
     137              :  * is not always reliable though, having known issues with `dm-multipath` for example.
     138              :  *
     139              :  * For this case most plugin functions consume the `extra` argument allowing
     140              :  * callers to specify arguments such as `--device=` for device type override. This
     141              :  * is only supported by the smartmontools plugin and ignored by the libatasmart
     142              :  * plugin.
     143              :  *
     144              :  * As a well kept secret libatasmart has historically supported device type override
     145              :  * via the `ID_ATA_SMART_ACCESS` udev property. There's no public C API for this and
     146              :  * libblockdev generally tends to avoid any udev interaction, leaving the burden
     147              :  * to callers.
     148              :  *
     149              :  * Valid values for this property include:
     150              :  * - `'sat16'`: 16 Byte SCSI ATA SAT Passthru
     151              :  * - `'sat12'`: 12 Byte SCSI ATA SAT Passthru
     152              :  * - `'linux-ide'`: Native Linux IDE
     153              :  * - `'sunplus'`: SunPlus USB/ATA bridges
     154              :  * - `'jmicron'`: JMicron USB/ATA bridges
     155              :  * - `'none'`: No access method, avoid any I/O and ignore the device
     156              :  * - `'auto'`: Autodetection based on parent subsystem (default)
     157              :  *
     158              :  * A common example to override QEMU ATA device type, which often requires legacy
     159              :  * IDE protocol:
     160              :  * |[
     161              :  * KERNEL=="sd*", ENV{ID_VENDOR}=="ATA", ENV{ID_MODEL}=="QEMU_HARDDISK", ENV{ID_ATA}=="1", ENV{ID_ATA_SMART_ACCESS}="linux-ide"
     162              :  * ]|
     163              :  *
     164              :  * An example of blacklisting a USB device, in case of errors caused by reading SMART data:
     165              :  * |[
     166              :  * ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", ENV{ID_ATA_SMART_ACCESS}="none"
     167              :  * ]|
     168              :  */
     169              : 
     170              : /**
     171              :  * bd_smart_error_quark: (skip)
     172              :  */
     173            0 : GQuark bd_smart_error_quark (void)
     174              : {
     175            0 :     return g_quark_from_static_string ("g-bd-smart-error-quark");
     176              : }
     177              : 
     178              : /**
     179              :  * bd_smart_init:
     180              :  *
     181              :  * Initializes the plugin. **This function is called automatically by the
     182              :  * library's initialization functions.**
     183              :  *
     184              :  */
     185            3 : gboolean bd_smart_init (void) {
     186              :     /* nothing to do here */
     187            3 :     return TRUE;
     188              : };
     189              : 
     190              : /**
     191              :  * bd_smart_close:
     192              :  *
     193              :  * Cleans up after the plugin. **This function is called automatically by the
     194              :  * library's functions that unload it.**
     195              :  *
     196              :  */
     197            3 : void bd_smart_close (void) {
     198              :     /* nothing to do here */
     199            3 : }
     200              : 
     201              : /**
     202              :  * bd_smart_ata_attribute_free: (skip)
     203              :  * @attr: (nullable): %BDSmartATAAttribute to free
     204              :  *
     205              :  * Frees @attr.
     206              :  */
     207            0 : void bd_smart_ata_attribute_free (BDSmartATAAttribute *attr) {
     208            0 :     if (attr == NULL)
     209            0 :         return;
     210            0 :     g_free (attr->name);
     211            0 :     g_free (attr->well_known_name);
     212            0 :     g_free (attr->pretty_value_string);
     213            0 :     g_free (attr);
     214              : }
     215              : 
     216              : /**
     217              :  * bd_smart_ata_attribute_copy: (skip)
     218              :  * @attr: (nullable): %BDSmartATAAttribute to copy
     219              :  *
     220              :  * Creates a new copy of @attr.
     221              :  */
     222            0 : BDSmartATAAttribute * bd_smart_ata_attribute_copy (BDSmartATAAttribute *attr) {
     223              :     BDSmartATAAttribute *new_attr;
     224              : 
     225            0 :     if (attr == NULL)
     226            0 :         return NULL;
     227              : 
     228            0 :     new_attr = g_new0 (BDSmartATAAttribute, 1);
     229            0 :     memcpy (new_attr, attr, sizeof (BDSmartATAAttribute));
     230            0 :     new_attr->name = g_strdup (attr->name);
     231            0 :     new_attr->well_known_name = g_strdup (attr->well_known_name);
     232            0 :     new_attr->pretty_value_string = g_strdup (attr->pretty_value_string);
     233              : 
     234            0 :     return new_attr;
     235              : }
     236              : 
     237              : /**
     238              :  * bd_smart_ata_free: (skip)
     239              :  * @data: (nullable): %BDSmartATA to free
     240              :  *
     241              :  * Frees @data.
     242              :  */
     243            0 : void bd_smart_ata_free (BDSmartATA *data) {
     244              :     BDSmartATAAttribute **attr;
     245              : 
     246            0 :     if (data == NULL)
     247            0 :         return;
     248              : 
     249            0 :     for (attr = data->attributes; attr && *attr; attr++)
     250            0 :         bd_smart_ata_attribute_free (*attr);
     251            0 :     g_free (data->attributes);
     252            0 :     g_free (data);
     253              : }
     254              : 
     255              : /**
     256              :  * bd_smart_ata_copy: (skip)
     257              :  * @data: (nullable): %BDSmartATA to copy
     258              :  *
     259              :  * Creates a new copy of @data.
     260              :  */
     261            0 : BDSmartATA * bd_smart_ata_copy (BDSmartATA *data) {
     262              :     BDSmartATA *new_data;
     263              :     BDSmartATAAttribute **attr;
     264              :     GPtrArray *ptr_array;
     265              : 
     266            0 :     if (data == NULL)
     267            0 :         return NULL;
     268              : 
     269            0 :     new_data = g_new0 (BDSmartATA, 1);
     270            0 :     memcpy (new_data, data, sizeof (BDSmartATA));
     271              : 
     272            0 :     ptr_array = g_ptr_array_new ();
     273            0 :     for (attr = data->attributes; attr && *attr; attr++)
     274            0 :         g_ptr_array_add (ptr_array, bd_smart_ata_attribute_copy (*attr));
     275            0 :     g_ptr_array_add (ptr_array, NULL);
     276            0 :     new_data->attributes = (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);
     277              : 
     278            0 :     return new_data;
     279              : }
     280              : 
     281              : /**
     282              :  * bd_smart_scsi_free: (skip)
     283              :  * @data: (nullable): %BDSmartSCSI to free
     284              :  *
     285              :  * Frees @data.
     286              :  */
     287            0 : void bd_smart_scsi_free (BDSmartSCSI *data) {
     288            0 :     if (data == NULL)
     289            0 :         return;
     290              : 
     291            0 :     g_free (data->scsi_ie_string);
     292            0 :     g_free (data);
     293              : }
     294              : 
     295              : /**
     296              :  * bd_smart_scsi_copy: (skip)
     297              :  * @data: (nullable): %BDSmartSCSI to copy
     298              :  *
     299              :  * Creates a new copy of @data.
     300              :  */
     301            0 : BDSmartSCSI * bd_smart_scsi_copy (BDSmartSCSI *data) {
     302              :     BDSmartSCSI *new_data;
     303              : 
     304            0 :     if (data == NULL)
     305            0 :         return NULL;
     306              : 
     307            0 :     new_data = g_new0 (BDSmartSCSI, 1);
     308            0 :     memcpy (new_data, data, sizeof (BDSmartSCSI));
     309            0 :     new_data->scsi_ie_string = g_strdup (data->scsi_ie_string);
     310              : 
     311            0 :     return new_data;
     312              : }
        

Generated by: LCOV version 2.0-1