LCOV - code coverage report
Current view: top level - plugins/smart - smartmontools.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 68.9 % 774 533
Test Date: 2026-01-26 13:19:28 Functions: 93.3 % 15 14
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2014-2023 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              : #include <inttypes.h>
      26              : 
      27              : #include <json-glib/json-glib.h>
      28              : 
      29              : #include <blockdev/utils.h>
      30              : #include <check_deps.h>
      31              : 
      32              : #include "smart.h"
      33              : #include "smart-private.h"
      34              : 
      35              : #define SMARTCTL_MIN_VERSION "7.0"
      36              : 
      37              : static volatile guint avail_deps = 0;
      38              : static GMutex deps_check_lock;
      39              : 
      40              : #define DEPS_SMART 0
      41              : #define DEPS_SMART_MASK (1 << DEPS_SMART)
      42              : #define DEPS_LAST 1
      43              : 
      44              : static const UtilDep deps[DEPS_LAST] = {
      45              :     { "smartctl", SMARTCTL_MIN_VERSION, NULL, "smartctl ([\\d\\.]+) .*" },
      46              : };
      47              : 
      48              : /**
      49              :  * bd_smart_check_deps:
      50              :  *
      51              :  * Returns: whether the plugin's runtime dependencies are satisfied or not
      52              :  *
      53              :  * Function checking plugin's runtime dependencies.
      54              :  *
      55              :  * Deprecated: 3.5: use %bd_smart_is_tech_avail instead
      56              :  */
      57            0 : gboolean bd_smart_check_deps (void) {
      58            0 :     return bd_smart_is_tech_avail (BD_SMART_TECH_ATA | BD_SMART_TECH_SCSI, 0, NULL);
      59              : }
      60              : 
      61              : /**
      62              :  * bd_smart_is_tech_avail:
      63              :  * @tech: the queried tech
      64              :  * @mode: a bit mask of queried modes of operation (#BDSmartTechMode) for @tech
      65              :  * @error: (out) (nullable): place to store error (details about why the @tech-@mode combination is not available)
      66              :  *
      67              :  * Returns: whether the @tech-@mode combination is available -- supported by the
      68              :  *          plugin implementation and having all the runtime dependencies available
      69              :  */
      70            3 : gboolean bd_smart_is_tech_avail (G_GNUC_UNUSED BDSmartTech tech, G_GNUC_UNUSED guint64 mode, GError **error) {
      71              :     /* all tech-mode combinations are supported by this implementation of the plugin */
      72            3 :     return check_deps (&avail_deps, DEPS_SMART_MASK, deps, DEPS_LAST, &deps_check_lock, error);
      73              : }
      74              : 
      75              : 
      76              : 
      77           50 : static const gchar * get_error_message_from_exit_code (gint exit_code) {
      78              :     /*
      79              :      * bit 0: Command line did not parse.
      80              :      * bit 1: Device open failed, device did not return an IDENTIFY DEVICE structure, or device is in a low-power mode
      81              :      * bit 2: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure
      82              :      */
      83              : 
      84           50 :     if (exit_code & 0x01)
      85            1 :         return "Command line did not parse.";
      86           49 :     if (exit_code & 0x02)
      87           35 :         return "Device open failed or device did not return an IDENTIFY DEVICE structure.";
      88           14 :     if (exit_code & 0x04)
      89           14 :         return "Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure.";
      90            0 :     return NULL;
      91              : }
      92              : 
      93          227 : static void lookup_well_known_attr (BDSmartATAAttribute *a,
      94              :                                     gchar **well_known_name,
      95              :                                     gint64 *pretty_value,
      96              :                                     BDSmartATAAttributeUnit *pretty_unit) {
      97          227 :     *well_known_name = g_strdup (well_known_attrs[a->id].libatasmart_name);
      98          227 :     if (*well_known_name) {
      99              :         /* verify matching attribute names */
     100              :         const gchar * const *n;
     101          215 :         gboolean trusted = FALSE;
     102              : 
     103          307 :         for (n = well_known_attrs[a->id].smartmontools_names; *n; n++)
     104          278 :             if (g_strcmp0 (*n, a->name) == 0) {
     105          186 :                 trusted = TRUE;
     106          186 :                 break;
     107              :             }
     108          215 :         if (trusted) {
     109          186 :             char *endptr = NULL;
     110              :             guint64 hour, min, sec, usec;
     111              : 
     112          186 :             *pretty_unit = well_known_attrs[a->id].unit;
     113          186 :             switch (well_known_attrs[a->id].unit) {
     114              :                 /* FIXME: we have the 64-bit raw value but no context how it's supposed
     115              :                  *        to be represented. This is defined in the smartmontools drivedb
     116              :                  *        yet not exposed over to JSON.
     117              :                  */
     118          148 :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN:
     119              :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_NONE:
     120              :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS:
     121              :                     /* try converting whatever possible and simply ignore the rest */
     122          148 :                     *pretty_value = g_ascii_strtoll (a->pretty_value_string, &endptr, 0);
     123          148 :                     if (! endptr || endptr == a->pretty_value_string)
     124            0 :                         *pretty_value = a->value_raw;
     125          148 :                     break;
     126           22 :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS:
     127              :                     /* possible formats as printed by ata_format_attr_raw_value():
     128              :                      *    "%lluh+%02llum (%u)"
     129              :                      *    "%lluh+%02llum+%02llus"
     130              :                      *    "%lluh+%02llum"
     131              :                      *    "%uh+%02um+%02u.%03us"
     132              :                      */
     133           22 :                     hour = min = sec = usec = 0;
     134           22 :                     if (sscanf (a->pretty_value_string, "%" PRIu64 "h+%" PRIu64 "m+%" PRIu64 "s.%" PRIu64 "s", &hour, &min, &sec, &usec) >= 2)
     135            0 :                         *pretty_value = ((hour * 60 + min) * 60 + sec) * 1000 + usec;
     136              :                     else
     137           22 :                         *pretty_value = a->value_raw;
     138           22 :                     break;
     139           14 :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN:
     140              :                     /* possible formats as printed by ata_format_attr_raw_value():
     141              :                      *   "%d"
     142              :                      *   "%d (Min/Max %d/%d)"
     143              :                      *   "%d (Min/Max %d/%d #%d)"
     144              :                      *   "%d (%d %d %d %d %d)"
     145              :                      *   "%d.%d"
     146              :                      */
     147           14 :                     *pretty_value = g_ascii_strtoll (a->pretty_value_string, &endptr, 0);
     148           14 :                     if (! endptr || endptr == a->pretty_value_string)
     149            0 :                         *pretty_value = a->value_raw;
     150              :                     else
     151              :                         /* temperature in degrees Celsius, need millikelvins */
     152           14 :                         *pretty_value = *pretty_value * 1000 + 273150;
     153           14 :                     break;
     154            2 :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT:
     155              :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT:
     156              :                 case BD_SMART_ATA_ATTRIBUTE_UNIT_MB:
     157              :                 default:
     158              :                     /* not implemented */
     159            2 :                     *pretty_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN;
     160            2 :                     break;
     161              :             }
     162              :         } else {
     163           29 :             g_free (*well_known_name);
     164           29 :             *well_known_name = NULL;
     165              :         }
     166              :     }
     167              : 
     168          227 :     if (*well_known_name == NULL) {
     169              :         /* not a well-known attribute or failed verification */
     170           41 :         *pretty_unit = 0;
     171           41 :         *pretty_value = a->value_raw;
     172              :     }
     173          227 : }
     174              : 
     175              : /* Returns num elements read or -1 in case of an error. */
     176          139 : static gint parse_int_array (JsonReader *reader, const gchar *key, gint64 *dest, gint max_count, GError **error) {
     177              :     gint count;
     178              :     int i;
     179              : 
     180          139 :     if (! json_reader_read_member (reader, key)) {
     181            0 :         g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     182            0 :                              json_reader_get_error (reader)->message);
     183            0 :         json_reader_end_member (reader);
     184            0 :         return -1;
     185              :     }
     186              : 
     187          139 :     count = MIN (max_count, json_reader_count_elements (reader));
     188          139 :     if (count < 0) {
     189            0 :         g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     190            0 :                              json_reader_get_error (reader)->message);
     191            0 :         return -1;
     192              :     }
     193              : 
     194          417 :     for (i = 0; i < count; i++) {
     195          278 :         if (! json_reader_read_element (reader, i)) {
     196            0 :             g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     197            0 :                                  json_reader_get_error (reader)->message);
     198            0 :             json_reader_end_element (reader);
     199            0 :             return -1;
     200              :         }
     201          278 :         dest[i] = json_reader_get_int_value (reader);
     202          278 :         json_reader_end_element (reader);
     203              :     }
     204              : 
     205          139 :     json_reader_end_member (reader);
     206          139 :     return count;
     207              : }
     208              : 
     209              : /* Returns null-terminated list of messages marked as severity=error */
     210           85 : static gchar ** parse_error_messages (JsonReader *reader) {
     211              :     GPtrArray *a;
     212              :     gint count;
     213              :     int i;
     214              : 
     215           85 :     if (! json_reader_read_member (reader, "smartctl")) {
     216            0 :         json_reader_end_member (reader);
     217            0 :         return NULL;
     218              :     }
     219           85 :     if (! json_reader_read_member (reader, "messages")) {
     220           50 :         json_reader_end_member (reader);
     221           50 :         json_reader_end_member (reader);
     222           50 :         return NULL;
     223              :     }
     224              : 
     225           35 :     a = g_ptr_array_new_full (0, g_free);
     226           35 :     count = json_reader_count_elements (reader);
     227              : 
     228           76 :     for (i = 0; count >= 0 && i < count; i++) {
     229           41 :         if (! json_reader_read_element (reader, i)) {
     230            0 :             json_reader_end_element (reader);
     231            0 :             g_ptr_array_free (a, TRUE);
     232            0 :             return NULL;
     233              :         }
     234           41 :         if (json_reader_is_object (reader)) {
     235           41 :             gboolean severity_error = FALSE;
     236              : 
     237           41 :             if (json_reader_read_member (reader, "severity"))
     238           41 :                 severity_error = g_strcmp0 ("error", json_reader_get_string_value (reader)) == 0;
     239           41 :             json_reader_end_member (reader);
     240              : 
     241           41 :             if (severity_error) {
     242           41 :                 if (json_reader_read_member (reader, "string")) {
     243           41 :                     const gchar *val = json_reader_get_string_value (reader);
     244           41 :                     if (val)
     245           41 :                         g_ptr_array_add (a, g_strdup (val));
     246              :                 }
     247           41 :                 json_reader_end_member (reader);
     248              :             }
     249              :         }
     250           41 :         json_reader_end_element (reader);
     251              :     }
     252           35 :     json_reader_end_member (reader);
     253           35 :     json_reader_end_member (reader);
     254              : 
     255           35 :     g_ptr_array_add (a, NULL);
     256           35 :     return (gchar **) g_ptr_array_free (a, FALSE);
     257              : }
     258              : 
     259              : 
     260              : #define MIN_JSON_FORMAT_VER 1     /* minimal json_format_version */
     261              : 
     262          130 : static gboolean parse_smartctl_error (gint status, const gchar *stdout, const gchar *stderr, JsonParser *parser, GError **error) {
     263              :     gint res;
     264              :     JsonReader *reader;
     265          130 :     gint64 ver_info[2] = { 0, 0 };
     266          130 :     GError *l_error = NULL;
     267              : 
     268          130 :     if ((!stdout || strlen (stdout) == 0) &&
     269            1 :         (!stderr || strlen (stderr) == 0)) {
     270            2 :         g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
     271            2 :                              (status & 0x07) ? get_error_message_from_exit_code (status) : "Empty response");
     272            2 :         return FALSE;
     273              :     }
     274              :     /* Expecting proper JSON output on stdout, take what has been received on stderr */
     275          128 :     if (!stdout || strlen (stdout) == 0) {
     276            0 :         g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
     277              :                              stderr);
     278            0 :         return FALSE;
     279              :     }
     280              : 
     281              :     /* Parse the JSON output */
     282          254 :     if (! json_parser_load_from_data (parser, stdout, -1, &l_error) ||
     283          126 :         ! json_parser_get_root (parser)) {
     284            2 :         g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     285            2 :                              l_error->message);
     286            2 :         g_error_free (l_error);
     287            2 :         return FALSE;
     288              :     }
     289          126 :     reader = json_reader_new (json_parser_get_root (parser));
     290              : 
     291              :     /* Verify the JSON output format */
     292          126 :     res = parse_int_array (reader, "json_format_version", ver_info, G_N_ELEMENTS (ver_info), error);
     293          126 :     if (res < 1) {
     294            0 :         g_prefix_error (error, "Error parsing version info: ");
     295            0 :         g_object_unref (reader);
     296            0 :         return FALSE;
     297              :     }
     298          126 :     if (ver_info[0] < MIN_JSON_FORMAT_VER) {
     299            1 :         g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     300              :                      "Reported smartctl JSON format version too low: %" G_GUINT64_FORMAT " (required: %d)",
     301              :                      ver_info[0], MIN_JSON_FORMAT_VER);
     302            1 :         g_object_unref (reader);
     303            1 :         return FALSE;
     304              :     }
     305          125 :     if (ver_info[0] > MIN_JSON_FORMAT_VER)
     306            0 :         g_warning ("Reported smartctl JSON format major version higher than expected, expect parse issues");
     307              : 
     308              :     /* Find out the return code and associated messages */
     309          125 :     if (status & 0x07) {
     310              :         gchar **messages;
     311              : 
     312           85 :         messages = parse_error_messages (reader);
     313          170 :         g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
     314           85 :                              messages && messages[0] ? messages[0] : get_error_message_from_exit_code (status));
     315           85 :         g_strfreev (messages);
     316           85 :         g_object_unref (reader);
     317           85 :         return FALSE;
     318              :     }
     319              : 
     320           40 :     g_object_unref (reader);
     321           40 :     return TRUE;
     322              : }
     323              : 
     324           13 : static BDSmartATAAttribute ** parse_ata_smart_attributes (JsonReader *reader, GError **error) {
     325              :     GPtrArray *ptr_array;
     326              :     gint count;
     327              :     int i;
     328              : 
     329           13 :     ptr_array = g_ptr_array_new_full (0, (GDestroyNotify) bd_smart_ata_attribute_free);
     330           13 :     count = json_reader_count_elements (reader);
     331          240 :     for (i = 0; count > 0 && i < count; i++) {
     332              :         BDSmartATAAttribute *attr;
     333              :         gint64 f;
     334              : 
     335          227 :         if (! json_reader_read_element (reader, i)) {
     336            0 :             g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     337              :                          "Error parsing smartctl JSON ata_smart_attributes[%d] element: %s",
     338            0 :                          i, json_reader_get_error (reader)->message);
     339            0 :             g_ptr_array_free (ptr_array, TRUE);
     340            0 :             json_reader_end_element (reader);
     341            0 :             return NULL;
     342              :         }
     343              : 
     344          227 :         attr = g_new0 (BDSmartATAAttribute, 1);
     345              : 
     346              : #define _READ_AND_CHECK(elem_name) \
     347              :         if (! json_reader_read_member (reader, elem_name)) { \
     348              :             g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT, \
     349              :                          "Error parsing smartctl JSON ata_smart_attributes[%d] element: %s", \
     350              :                          i, json_reader_get_error (reader)->message); \
     351              :             g_ptr_array_free (ptr_array, TRUE); \
     352              :             bd_smart_ata_attribute_free (attr); \
     353              :             json_reader_end_member (reader); \
     354              :             json_reader_end_element (reader); \
     355              :             return NULL; \
     356              :         }
     357              : 
     358          227 :         _READ_AND_CHECK ("id");
     359          227 :         attr->id = json_reader_get_int_value (reader);
     360          227 :         json_reader_end_member (reader);
     361              : 
     362          227 :         _READ_AND_CHECK ("name");
     363          227 :         attr->name = g_strdup (json_reader_get_string_value (reader));
     364          227 :         json_reader_end_member (reader);
     365              : 
     366          227 :         _READ_AND_CHECK ("value");
     367          227 :         attr->value = json_reader_get_int_value (reader);
     368          227 :         json_reader_end_member (reader);
     369              : 
     370          227 :         _READ_AND_CHECK ("worst");
     371          227 :         attr->worst = json_reader_get_int_value (reader);
     372          227 :         json_reader_end_member (reader);
     373              : 
     374          227 :         _READ_AND_CHECK ("thresh");
     375          227 :         attr->threshold = json_reader_get_int_value (reader);
     376          227 :         json_reader_end_member (reader);
     377              : 
     378          227 :         _READ_AND_CHECK ("when_failed");
     379          227 :         if (g_strcmp0 (json_reader_get_string_value (reader), "past") == 0)
     380            0 :             attr->failed_past = TRUE;
     381              :         else
     382          227 :         if (g_strcmp0 (json_reader_get_string_value (reader), "now") == 0)
     383            0 :             attr->failing_now = TRUE;
     384          227 :         json_reader_end_member (reader);
     385              : 
     386          227 :         _READ_AND_CHECK ("raw");
     387          227 :         _READ_AND_CHECK ("value");
     388          227 :         attr->value_raw = json_reader_get_int_value (reader);
     389          227 :         json_reader_end_member (reader);
     390          227 :         _READ_AND_CHECK ("string");
     391          227 :         attr->pretty_value_string = g_strdup (json_reader_get_string_value (reader));
     392          227 :         json_reader_end_member (reader);
     393          227 :         json_reader_end_member (reader);
     394              : 
     395          227 :         _READ_AND_CHECK ("flags");
     396          227 :         _READ_AND_CHECK ("value");
     397          227 :         f = json_reader_get_int_value (reader);
     398          227 :         if (f & 0x01)
     399           80 :             attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_PREFAILURE;
     400          227 :         if (f & 0x02)
     401          200 :             attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_ONLINE;
     402          227 :         if (f & 0x04)
     403           33 :             attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_PERFORMANCE;
     404          227 :         if (f & 0x08)
     405           37 :             attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_ERROR_RATE;
     406          227 :         if (f & 0x10)
     407          141 :             attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_EVENT_COUNT;
     408          227 :         if (f & 0x20)
     409          118 :             attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_SELF_PRESERVING;
     410          227 :         if (f & 0xffc0)
     411            0 :             attr->flags |= BD_SMART_ATA_ATTRIBUTE_FLAG_OTHER;
     412          227 :         json_reader_end_member (reader);
     413          227 :         json_reader_end_member (reader);
     414          227 :         json_reader_end_element (reader);
     415              : 
     416              : #undef _READ_AND_CHECK
     417              : 
     418          227 :         lookup_well_known_attr (attr,
     419              :                                 &attr->well_known_name,
     420              :                                 &attr->pretty_value,
     421              :                                 &attr->pretty_value_unit);
     422          227 :         g_ptr_array_add (ptr_array, attr);
     423              :     }
     424              : 
     425           13 :     g_ptr_array_add (ptr_array, NULL);
     426           13 :     return (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);
     427              : }
     428              : 
     429           17 : static BDSmartATA * parse_ata_smart (JsonParser *parser, GError **error) {
     430              :     BDSmartATA *data;
     431              :     JsonReader *reader;
     432              : 
     433           17 :     data = g_new0 (BDSmartATA, 1);
     434           17 :     reader = json_reader_new (json_parser_get_root (parser));
     435              : 
     436              :     /* smart_support section */
     437           17 :     if (json_reader_read_member (reader, "smart_support")) {
     438           17 :         if (json_reader_read_member (reader, "available"))
     439           17 :             data->smart_supported = json_reader_get_boolean_value (reader);
     440           17 :         json_reader_end_member (reader);
     441           17 :         if (json_reader_read_member (reader, "enabled"))
     442           17 :             data->smart_enabled = json_reader_get_boolean_value (reader);
     443           17 :         json_reader_end_member (reader);
     444              :     }
     445           17 :     json_reader_end_member (reader);
     446              : 
     447              :     /* smart_status section */
     448           17 :     if (json_reader_read_member (reader, "smart_status")) {
     449           17 :         if (json_reader_read_member (reader, "passed"))
     450           17 :             data->overall_status_passed = json_reader_get_boolean_value (reader);
     451           17 :         json_reader_end_member (reader);
     452              :     }
     453           17 :     json_reader_end_member (reader);
     454              : 
     455              :     /* ata_smart_data section */
     456           17 :     if (! json_reader_read_member (reader, "ata_smart_data")) {
     457            4 :         g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     458              :                      "Error parsing smartctl JSON data: %s",
     459            4 :                      json_reader_get_error (reader)->message);
     460            4 :         g_object_unref (reader);
     461            4 :         bd_smart_ata_free (data);
     462            4 :         return NULL;
     463              :     }
     464           13 :     if (json_reader_read_member (reader, "offline_data_collection")) {
     465           13 :         if (json_reader_read_member (reader, "status")) {
     466           13 :             if (json_reader_read_member (reader, "value")) {
     467           13 :                 gint64 val = json_reader_get_int_value (reader);
     468              : 
     469           13 :                 switch (val & 0x7f) {
     470            4 :                     case 0x00:
     471            4 :                         data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED;
     472            4 :                         break;
     473            7 :                     case 0x02:
     474            7 :                         data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR;
     475            7 :                         break;
     476            0 :                     case 0x03:
     477            0 :                         if (val == 0x03)
     478            0 :                             data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS;
     479              :                         else
     480            0 :                             data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED;
     481            0 :                         break;
     482            2 :                     case 0x04:
     483            2 :                         data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR;
     484            2 :                         break;
     485            0 :                     case 0x05:
     486            0 :                         data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR;
     487            0 :                         break;
     488            0 :                     case 0x06:
     489            0 :                         data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR;
     490            0 :                         break;
     491            0 :                     default:
     492            0 :                         if ((val & 0x7f) >= 0x40)
     493            0 :                             data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC;
     494              :                         else
     495            0 :                             data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_RESERVED;
     496            0 :                         break;
     497              :                 }
     498           13 :                 data->auto_offline_data_collection_enabled = val & 0x80;
     499              :             }
     500           13 :             json_reader_end_member (reader);
     501              :         }
     502           13 :         json_reader_end_member (reader);
     503           13 :         if (json_reader_read_member (reader, "completion_seconds"))
     504           13 :             data->offline_data_collection_completion = json_reader_get_int_value (reader);
     505           13 :         json_reader_end_member (reader);
     506              :     }
     507           13 :     json_reader_end_member (reader);  /* offline_data_collection */
     508              : 
     509           13 :     if (json_reader_read_member (reader, "self_test")) {
     510           13 :         if (json_reader_read_member (reader, "status")) {
     511           13 :             if (json_reader_read_member (reader, "value")) {
     512           13 :                 gint64 val = json_reader_get_int_value (reader);
     513              : 
     514           13 :                 switch (val >> 4) {
     515           13 :                     case 0x00:
     516           13 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR;
     517           13 :                         break;
     518            0 :                     case 0x01:
     519            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST;
     520            0 :                         break;
     521            0 :                     case 0x02:
     522            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET;
     523            0 :                         break;
     524            0 :                     case 0x03:
     525            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_FATAL;
     526            0 :                         break;
     527            0 :                     case 0x04:
     528            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN;
     529            0 :                         break;
     530            0 :                     case 0x05:
     531            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL;
     532            0 :                         break;
     533            0 :                     case 0x06:
     534            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO;
     535            0 :                         break;
     536            0 :                     case 0x07:
     537            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ;
     538            0 :                         break;
     539            0 :                     case 0x08:
     540            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING;
     541            0 :                         break;
     542            0 :                     case 0x0f:
     543            0 :                         data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS;
     544            0 :                         data->self_test_percent_remaining = (val & 0x0f) * 10;
     545            0 :                         break;
     546              :                 }
     547              :             }
     548           13 :             json_reader_end_member (reader);
     549              :         }
     550           13 :         json_reader_end_member (reader);
     551           13 :         if (json_reader_read_member (reader, "polling_minutes")) {
     552           13 :             if (json_reader_read_member (reader, "short"))
     553           13 :                 data->self_test_polling_short = json_reader_get_int_value (reader);
     554           13 :             json_reader_end_member (reader);
     555           13 :             if (json_reader_read_member (reader, "extended"))
     556           13 :                 data->self_test_polling_extended = json_reader_get_int_value (reader);
     557           13 :             json_reader_end_member (reader);
     558           13 :             if (json_reader_read_member (reader, "conveyance"))
     559            4 :                 data->self_test_polling_conveyance = json_reader_get_int_value (reader);
     560           13 :             json_reader_end_member (reader);
     561              :         }
     562           13 :         json_reader_end_member (reader);
     563              :     }
     564           13 :     json_reader_end_member (reader);  /* self_test */
     565              : 
     566           13 :     if (json_reader_read_member (reader, "capabilities")) {
     567           13 :         gint64 val[2] = { 0, 0 };
     568              : 
     569           13 :         if (parse_int_array (reader, "values", val, G_N_ELEMENTS (val), NULL) == G_N_ELEMENTS (val)) {
     570           13 :             if (val[0] == 0x00)
     571            0 :                 data->offline_data_collection_capabilities = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_NOT_SUPPORTED;
     572              :             else {
     573           13 :                 if (val[0] & 0x01)
     574           13 :                     data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_EXEC_OFFLINE_IMMEDIATE;
     575              :                 /* 0x02 is deprecated - SupportAutomaticTimer */
     576           13 :                 if (val[0] & 0x04)
     577            0 :                     data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_ABORT;
     578           13 :                 if (val[0] & 0x08)
     579           13 :                     data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_OFFLINE_SURFACE_SCAN;
     580           13 :                 if (val[0] & 0x10)
     581           13 :                     data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELF_TEST;
     582           13 :                 if (val[0] & 0x20)
     583            4 :                     data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_CONVEYANCE_SELF_TEST;
     584           13 :                 if (val[0] & 0x40)
     585           13 :                     data->offline_data_collection_capabilities |= BD_SMART_ATA_OFFLINE_DATA_COLLECTION_CAP_SELECTIVE_SELF_TEST;
     586              :             }
     587           13 :             if (val[1] & 0x01)
     588           13 :                 data->smart_capabilities |= BD_SMART_ATA_CAP_ATTRIBUTE_AUTOSAVE;
     589           13 :             if (val[1] & 0x02)
     590           13 :                 data->smart_capabilities |= BD_SMART_ATA_CAP_AUTOSAVE_TIMER;
     591              :         }
     592           13 :         if (json_reader_read_member (reader, "error_logging_supported"))
     593           13 :             if (json_reader_get_boolean_value (reader))
     594           13 :                 data->smart_capabilities |= BD_SMART_ATA_CAP_ERROR_LOGGING;
     595           13 :         json_reader_end_member (reader);
     596           13 :         if (json_reader_read_member (reader, "gp_logging_supported"))
     597           13 :             if (json_reader_get_boolean_value (reader))
     598           13 :                 data->smart_capabilities |= BD_SMART_ATA_CAP_GP_LOGGING;
     599           13 :         json_reader_end_member (reader);
     600              :     }
     601           13 :     json_reader_end_member (reader);  /* capabilities */
     602           13 :     json_reader_end_member (reader);  /* ata_smart_data */
     603              : 
     604              :     /* ata_smart_attributes section */
     605           26 :     if (! json_reader_read_member (reader, "ata_smart_attributes") ||
     606           26 :         ! json_reader_read_member (reader, "table") ||
     607           13 :         ! json_reader_is_array (reader)) {
     608            0 :         g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
     609              :                      "Error parsing smartctl JSON data: %s",
     610            0 :                      json_reader_get_error (reader)->message);
     611            0 :         g_object_unref (reader);
     612            0 :         bd_smart_ata_free (data);
     613            0 :         return NULL;
     614              :     }
     615           13 :     data->attributes = parse_ata_smart_attributes (reader, error);
     616           13 :     if (! data->attributes) {
     617            0 :         g_object_unref (reader);
     618            0 :         bd_smart_ata_free (data);
     619            0 :         return NULL;
     620              :     }
     621           13 :     json_reader_end_member (reader);  /* table */
     622           13 :     json_reader_end_member (reader);  /* ata_smart_attributes */
     623              : 
     624              :     /* power_on_time section */
     625           13 :     if (json_reader_read_member (reader, "power_on_time")) {
     626           13 :         if (json_reader_read_member (reader, "hours"))
     627           13 :             data->power_on_time += json_reader_get_int_value (reader) * 60;
     628           13 :         json_reader_end_member (reader);
     629           13 :         if (json_reader_read_member (reader, "minutes"))
     630            0 :             data->power_on_time += json_reader_get_int_value (reader);
     631           13 :         json_reader_end_member (reader);
     632              :     }
     633           13 :     json_reader_end_member (reader);
     634              : 
     635              :     /* power_cycle_count section */
     636           13 :     if (json_reader_read_member (reader, "power_cycle_count"))
     637           13 :         data->power_cycle_count = json_reader_get_int_value (reader);
     638           13 :     json_reader_end_member (reader);
     639              : 
     640              :     /* temperature section */
     641           13 :     if (json_reader_read_member (reader, "temperature")) {
     642           13 :         if (json_reader_read_member (reader, "current"))
     643           13 :             data->temperature = json_reader_get_int_value (reader) + 273;
     644           13 :         json_reader_end_member (reader);
     645              :     }
     646           13 :     json_reader_end_member (reader);
     647              : 
     648           13 :     g_object_unref (reader);
     649           13 :     return data;
     650              : }
     651              : 
     652              : 
     653            6 : static BDSmartSCSI * parse_scsi_smart (JsonParser *parser, G_GNUC_UNUSED GError **error) {
     654              :     BDSmartSCSI *data;
     655              :     JsonReader *reader;
     656              : 
     657            6 :     data = g_new0 (BDSmartSCSI, 1);
     658            6 :     reader = json_reader_new (json_parser_get_root (parser));
     659              : 
     660              :     /* smart_support section */
     661            6 :     if (json_reader_read_member (reader, "smart_support")) {
     662            6 :         if (json_reader_read_member (reader, "available"))
     663            6 :             data->smart_supported = json_reader_get_boolean_value (reader);
     664            6 :         json_reader_end_member (reader);
     665            6 :         if (json_reader_read_member (reader, "enabled"))
     666            6 :             data->smart_enabled = json_reader_get_boolean_value (reader);
     667            6 :         json_reader_end_member (reader);
     668              :     }
     669            6 :     json_reader_end_member (reader);
     670              : 
     671              :     /* smart_status section */
     672            6 :     if (json_reader_read_member (reader, "smart_status")) {
     673            6 :         if (json_reader_read_member (reader, "passed"))
     674            6 :             data->overall_status_passed = json_reader_get_boolean_value (reader);
     675            6 :         json_reader_end_member (reader);
     676            6 :         if (json_reader_read_member (reader, "scsi")) {
     677            0 :             gint64 asc = -1;
     678            0 :             gint64 ascq = -1;
     679              : 
     680            0 :             if (json_reader_read_member (reader, "asc")) {
     681            0 :                 asc = json_reader_get_int_value (reader);
     682            0 :                 data->scsi_ie_asc = asc;
     683              :             }
     684            0 :             json_reader_end_member (reader);
     685            0 :             if (json_reader_read_member (reader, "ascq")) {
     686            0 :                 ascq = json_reader_get_int_value (reader);
     687            0 :                 data->scsi_ie_ascq = ascq;
     688              :             }
     689            0 :             json_reader_end_member (reader);
     690            0 :             if (json_reader_read_member (reader, "ie_string"))
     691            0 :                 data->scsi_ie_string = g_strdup (json_reader_get_string_value (reader));
     692            0 :             json_reader_end_member (reader);
     693              : 
     694            0 :             if (asc == 0xb && ascq >= 0) {
     695            0 :                 switch (ascq) {
     696            0 :                     case 0x00:
     697            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ABORTED_COMMAND;
     698            0 :                         break;
     699            0 :                     case 0x01:
     700            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_TEMPERATURE_EXCEEDED;
     701            0 :                         break;
     702            0 :                     case 0x02:
     703            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_ENCLOSURE_DEGRADED;
     704            0 :                         break;
     705            0 :                     case 0x03:
     706            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SELFTEST_FAILED;
     707            0 :                         break;
     708            0 :                     case 0x04:
     709            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_PRESCAN_MEDIUM_ERROR;
     710            0 :                         break;
     711            0 :                     case 0x05:
     712            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_BACKGROUND_SCAN_MEDIUM_ERROR;
     713            0 :                         break;
     714            0 :                     case 0x06:
     715            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_VOLATILE;
     716            0 :                         break;
     717            0 :                     case 0x07:
     718            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_NV_CACHE_DEGRADED_POWER;
     719            0 :                         break;
     720            0 :                     case 0x08:
     721            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_POWER_LOSS_EXPECTED;
     722            0 :                         break;
     723            0 :                     case 0x09:
     724            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_STATISTICS_NOTIFICATION;
     725            0 :                         break;
     726            0 :                     case 0x0a:
     727            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_TEMP;
     728            0 :                         break;
     729            0 :                     case 0x0b:
     730            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_TEMP;
     731            0 :                         break;
     732            0 :                     case 0x0c:
     733            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_TEMP;
     734            0 :                         break;
     735            0 :                     case 0x0d:
     736            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_TEMP;
     737            0 :                         break;
     738            0 :                     case 0x0e:
     739            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_CRITICAL_HUMIDITY;
     740            0 :                         break;
     741            0 :                     case 0x0f:
     742            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_CRITICAL_HUMIDITY;
     743            0 :                         break;
     744            0 :                     case 0x10:
     745            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HIGH_OPERATING_HUMIDITY;
     746            0 :                         break;
     747            0 :                     case 0x11:
     748            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOW_OPERATING_HUMIDITY;
     749            0 :                         break;
     750            0 :                     case 0x12:
     751            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SECURITY_RISK;
     752            0 :                         break;
     753            0 :                     case 0x13:
     754            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MICROCODE_SIGNATURE_VALIDATION_FAILURE;
     755            0 :                         break;
     756            0 :                     case 0x14:
     757            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_PHYSICAL_ELEMENT_STATUS_CHANGE;
     758            0 :                         break;
     759            0 :                     default:
     760            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED;
     761            0 :                         break;
     762              :                 }
     763            0 :             } else if (asc == 0x5d && ascq >= 0) {
     764            0 :                 switch (ascq) {
     765            0 :                     case 0x00:
     766              :                     case 0xff:
     767            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FAILURE_PREDICTION_THRESH;
     768            0 :                         break;
     769            0 :                     case 0x01:
     770            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_FAILURE_PREDICTION_THRESH;
     771            0 :                         break;
     772            0 :                     case 0x02:
     773            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_LOGICAL_UNIT_FAILURE_PREDICTION_THRESH;
     774            0 :                         break;
     775            0 :                     case 0x03:
     776            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPARE_EXHAUSTION_PREDICTION_THRESH;
     777            0 :                         break;
     778            0 :                     case 0x73:
     779            0 :                         data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_MEDIA_ENDURANCE_LIMIT;
     780            0 :                         break;
     781            0 :                     default:
     782            0 :                         if (ascq >= 0x10 && ascq <= 0x1d)
     783            0 :                             data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_HARDWARE_IMPENDING_FAILURE;
     784            0 :                         else if (ascq >= 0x20 && ascq <= 0x2c)
     785            0 :                             data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_CONTROLLER_IMPENDING_FAILURE;
     786            0 :                         else if (ascq >= 0x30 && ascq <= 0x3c)
     787            0 :                             data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_DATA_CHANNEL_IMPENDING_FAILURE;
     788            0 :                         else if (ascq >= 0x40 && ascq <= 0x4c)
     789            0 :                             data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SERVO_IMPENDING_FAILURE;
     790            0 :                         else if (ascq >= 0x50 && ascq <= 0x5c)
     791            0 :                             data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_SPINDLE_IMPENDING_FAILURE;
     792            0 :                         else if (ascq >= 0x60 && ascq <= 0x6c)
     793            0 :                             data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_FIRMWARE_IMPENDING_FAILURE;
     794              :                         else
     795            0 :                             data->scsi_ie = BD_SMART_SCSI_INFORMATIONAL_EXCEPTION_UNSPECIFIED;
     796            0 :                         break;
     797              :                 }
     798              :             }
     799              :         }
     800            6 :         json_reader_end_member (reader);
     801              :     }
     802            6 :     json_reader_end_member (reader);
     803              : 
     804              :     /* temperature_warning section */
     805            6 :     if (json_reader_read_member (reader, "temperature_warning")) {
     806            6 :         if (json_reader_read_member (reader, "enabled"))
     807            6 :             data->temperature_warning_enabled = json_reader_get_boolean_value (reader);
     808            6 :         json_reader_end_member (reader);
     809              :     }
     810            6 :     json_reader_end_member (reader);
     811              : 
     812              :     /* temperature section */
     813            6 :     if (json_reader_read_member (reader, "temperature")) {
     814            6 :         if (json_reader_read_member (reader, "current"))
     815            6 :             data->temperature = json_reader_get_int_value (reader) + 273;
     816            6 :         json_reader_end_member (reader);
     817            6 :         if (json_reader_read_member (reader, "drive_trip"))
     818            6 :             data->temperature_drive_trip = json_reader_get_int_value (reader) + 273;
     819            6 :         json_reader_end_member (reader);
     820              :     }
     821            6 :     json_reader_end_member (reader);
     822              : 
     823              :     /* scsi_background_scan section */
     824            6 :     if (json_reader_read_member (reader, "scsi_background_scan")) {
     825            1 :         if (json_reader_read_member (reader, "status")) {
     826            1 :             if (json_reader_read_member (reader, "value")) {
     827            1 :                 guint64 val = json_reader_get_int_value (reader);
     828              : 
     829            1 :                 switch (val) {
     830            1 :                     case 0x00:
     831            1 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_NO_SCANS_ACTIVE;
     832            1 :                         break;
     833            0 :                     case 0x01:
     834            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_SCAN_ACTIVE;
     835            0 :                         break;
     836            0 :                     case 0x02:
     837            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_PRESCAN_ACTIVE;
     838            0 :                         break;
     839            0 :                     case 0x03:
     840            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_FATAL;
     841            0 :                         break;
     842            0 :                     case 0x04:
     843            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_PATTERN_VENDOR_SPECIFIC;
     844            0 :                         break;
     845            0 :                     case 0x05:
     846            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_ERROR_PLIST;
     847            0 :                         break;
     848            0 :                     case 0x06:
     849            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_VENDOR_SPECIFIC;
     850            0 :                         break;
     851            0 :                     case 0x07:
     852            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_HALTED_TEMPERATURE;
     853            0 :                         break;
     854            0 :                     case 0x08:
     855            0 :                         data->background_scan_status = BD_SMART_SCSI_BACKGROUND_SCAN_STATUS_BMS_TIMER;
     856            0 :                         break;
     857            0 :                     default:
     858              :                         /* just copy the value, it corresponds to the above anyway */
     859            0 :                         data->background_scan_status = val;
     860              :                 }
     861              :             }
     862            1 :             json_reader_end_member (reader);
     863            1 :             if (json_reader_read_member (reader, "scan_progress")) {
     864            1 :                 const gchar *val = json_reader_get_string_value (reader);
     865            1 :                 float d = 0.0;
     866              : 
     867            1 :                 if (sscanf (val, "%f%%", &d) == 1)
     868            1 :                     data->background_scan_progress = d;
     869              :             }
     870            1 :             json_reader_end_member (reader);
     871            1 :             if (json_reader_read_member (reader, "number_scans_performed"))
     872            1 :                 data->background_scan_runs = json_reader_get_int_value (reader);
     873            1 :             json_reader_end_member (reader);
     874            1 :             if (json_reader_read_member (reader, "number_medium_scans_performed"))
     875            1 :                 data->background_medium_scan_runs = json_reader_get_int_value (reader);
     876            1 :             json_reader_end_member (reader);
     877              : 
     878              :         }
     879            1 :         json_reader_end_member (reader);
     880              :     }
     881            6 :     json_reader_end_member (reader);
     882              : 
     883              :     /* scsi_start_stop_cycle_counter section */
     884            6 :     if (json_reader_read_member (reader, "scsi_start_stop_cycle_counter")) {
     885            6 :         if (json_reader_read_member (reader, "specified_cycle_count_over_device_lifetime"))
     886            5 :             data->start_stop_cycle_lifetime = json_reader_get_int_value (reader);
     887            6 :         json_reader_end_member (reader);
     888            6 :         if (json_reader_read_member (reader, "accumulated_start_stop_cycles"))
     889            5 :             data->start_stop_cycle_count = json_reader_get_int_value (reader);
     890            6 :         json_reader_end_member (reader);
     891            6 :         if (json_reader_read_member (reader, "specified_load_unload_count_over_device_lifetime"))
     892            5 :             data->load_unload_cycle_lifetime = json_reader_get_int_value (reader);
     893            6 :         json_reader_end_member (reader);
     894            6 :         if (json_reader_read_member (reader, "accumulated_load_unload_cycles"))
     895            5 :             data->load_unload_cycle_count = json_reader_get_int_value (reader);
     896            6 :         json_reader_end_member (reader);
     897              :     }
     898            6 :     json_reader_end_member (reader);
     899              : 
     900              :     /* scsi_grown_defect_list section */
     901            6 :     if (json_reader_read_member (reader, "scsi_grown_defect_list"))
     902            5 :         data->scsi_grown_defect_list = json_reader_get_int_value (reader);
     903            6 :     json_reader_end_member (reader);
     904              : 
     905              :     /* scsi_error_counter_log section */
     906            6 :     if (json_reader_read_member (reader, "scsi_error_counter_log")) {
     907            6 :         if (json_reader_read_member (reader, "read")) {
     908            6 :             if (json_reader_read_member (reader, "errors_corrected_by_eccfast"))
     909            6 :                 data->read_errors_corrected_eccfast = json_reader_get_int_value (reader);
     910            6 :             json_reader_end_member (reader);
     911            6 :             if (json_reader_read_member (reader, "errors_corrected_by_eccdelayed"))
     912            6 :                 data->read_errors_corrected_eccdelayed = json_reader_get_int_value (reader);
     913            6 :             json_reader_end_member (reader);
     914            6 :             if (json_reader_read_member (reader, "errors_corrected_by_rereads_rewrites"))
     915            6 :                 data->read_errors_corrected_rereads = json_reader_get_int_value (reader);
     916            6 :             json_reader_end_member (reader);
     917            6 :             if (json_reader_read_member (reader, "total_errors_corrected"))
     918            6 :                 data->read_errors_corrected_total = json_reader_get_int_value (reader);
     919            6 :             json_reader_end_member (reader);
     920            6 :             if (json_reader_read_member (reader, "total_uncorrected_errors"))
     921            6 :                 data->read_errors_uncorrected = json_reader_get_int_value (reader);
     922            6 :             json_reader_end_member (reader);
     923            6 :             if (json_reader_read_member (reader, "gigabytes_processed")) {
     924            6 :                 const gchar *val = json_reader_get_string_value (reader);
     925            6 :                 gdouble d = 0.0;
     926              : 
     927            6 :                 if (val)
     928            6 :                     d = g_ascii_strtod (val, NULL);
     929            6 :                 data->read_processed_bytes = d * 1000000000;
     930              :             }
     931            6 :             json_reader_end_member (reader);
     932              :         }
     933            6 :         json_reader_end_member (reader);
     934            6 :         if (json_reader_read_member (reader, "write")) {
     935            6 :             if (json_reader_read_member (reader, "errors_corrected_by_eccfast"))
     936            6 :                 data->write_errors_corrected_eccfast = json_reader_get_int_value (reader);
     937            6 :             json_reader_end_member (reader);
     938            6 :             if (json_reader_read_member (reader, "errors_corrected_by_eccdelayed"))
     939            6 :                 data->write_errors_corrected_eccdelayed = json_reader_get_int_value (reader);
     940            6 :             json_reader_end_member (reader);
     941            6 :             if (json_reader_read_member (reader, "errors_corrected_by_rereads_rewrites"))
     942            6 :                 data->write_errors_corrected_rewrites = json_reader_get_int_value (reader);
     943            6 :             json_reader_end_member (reader);
     944            6 :             if (json_reader_read_member (reader, "total_errors_corrected"))
     945            6 :                 data->write_errors_corrected_total = json_reader_get_int_value (reader);
     946            6 :             json_reader_end_member (reader);
     947            6 :             if (json_reader_read_member (reader, "total_uncorrected_errors"))
     948            6 :                 data->write_errors_uncorrected = json_reader_get_int_value (reader);
     949            6 :             json_reader_end_member (reader);
     950            6 :             if (json_reader_read_member (reader, "gigabytes_processed")) {
     951            6 :                 const gchar *val = json_reader_get_string_value (reader);
     952            6 :                 gdouble d = 0.0;
     953              : 
     954            6 :                 if (val)
     955            6 :                     d = g_ascii_strtod (val, NULL);
     956            6 :                 data->write_processed_bytes = d * 1000000000;
     957              :             }
     958            6 :             json_reader_end_member (reader);
     959              :         }
     960            6 :         json_reader_end_member (reader);
     961              :     }
     962            6 :     json_reader_end_member (reader);
     963              : 
     964              :     /* power_on_time section */
     965            6 :     if (json_reader_read_member (reader, "power_on_time")) {
     966            6 :         if (json_reader_read_member (reader, "hours"))
     967            6 :             data->power_on_time += json_reader_get_int_value (reader) * 60;
     968            6 :         json_reader_end_member (reader);
     969            6 :         if (json_reader_read_member (reader, "minutes"))
     970            6 :             data->power_on_time += json_reader_get_int_value (reader);
     971            6 :         json_reader_end_member (reader);
     972              :     }
     973            6 :     json_reader_end_member (reader);
     974              : 
     975            6 :     g_object_unref (reader);
     976            6 :     return data;
     977              : }
     978              : 
     979              : 
     980              : /**
     981              :  * bd_smart_ata_get_info:
     982              :  * @device: device to check.
     983              :  * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
     984              :  * @error: (out) (optional): place to store error (if any).
     985              :  *
     986              :  * Retrieve SMART information from the drive.
     987              :  *
     988              :  * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
     989              :  *
     990              :  * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
     991              :  */
     992           24 : BDSmartATA * bd_smart_ata_get_info (const gchar *device, const BDExtraArg **extra, GError **error) {
     993           24 :     const gchar *args[8] = { "smartctl", "--info", "--health", "--capabilities", "--attributes", "--json", device, NULL };
     994           24 :     gint status = 0;
     995           24 :     gchar *stdout = NULL;
     996           24 :     gchar *stderr = NULL;
     997              :     JsonParser *parser;
     998           24 :     BDSmartATA *data = NULL;
     999              :     gboolean ret;
    1000              : 
    1001           24 :     if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
    1002            0 :         g_prefix_error (error, "Error getting ATA SMART info: ");
    1003            0 :         return NULL;
    1004              :     }
    1005              : 
    1006           24 :     if (stdout)
    1007           24 :         g_strstrip (stdout);
    1008           24 :     if (stderr)
    1009           24 :         g_strstrip (stderr);
    1010              : 
    1011           24 :     parser = json_parser_new ();
    1012           24 :     ret = parse_smartctl_error (status, stdout, stderr, parser, error);
    1013           24 :     g_free (stdout);
    1014           24 :     g_free (stderr);
    1015           24 :     if (! ret) {
    1016           13 :         g_prefix_error (error, "Error getting ATA SMART info: ");
    1017           13 :         g_object_unref (parser);
    1018           13 :         return NULL;
    1019              :     }
    1020              : 
    1021           11 :     data = parse_ata_smart (parser, error);
    1022           11 :     g_object_unref (parser);
    1023              : 
    1024           11 :     return data;
    1025              : }
    1026              : 
    1027              : /**
    1028              :  * bd_smart_ata_get_info_from_data:
    1029              :  * @data: (array length=data_len): binary data to parse.
    1030              :  * @data_len: length of the data supplied.
    1031              :  * @error: (out) (optional): place to store error (if any).
    1032              :  *
    1033              :  * Retrieve SMART information from the supplied data.
    1034              :  *
    1035              :  * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
    1036              :  *
    1037              :  * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
    1038              :  */
    1039            8 : BDSmartATA * bd_smart_ata_get_info_from_data (const guint8 *data, gsize data_len, GError **error) {
    1040              :     JsonParser *parser;
    1041              :     gchar *stdout;
    1042            8 :     BDSmartATA *ata_data = NULL;
    1043              :     gboolean ret;
    1044              : 
    1045            8 :     g_warn_if_fail (data != NULL);
    1046            8 :     g_warn_if_fail (data_len > 0);
    1047              : 
    1048            8 :     stdout = g_strndup ((gchar *)data, data_len);
    1049            8 :     g_strstrip (stdout);
    1050              : 
    1051            8 :     parser = json_parser_new ();
    1052            8 :     ret = parse_smartctl_error (0, stdout, NULL, parser, error);
    1053            8 :     g_free (stdout);
    1054            8 :     if (! ret) {
    1055            2 :         g_prefix_error (error, "Error getting ATA SMART info: ");
    1056            2 :         g_object_unref (parser);
    1057            2 :         return NULL;
    1058              :     }
    1059              : 
    1060            6 :     ata_data = parse_ata_smart (parser, error);
    1061            6 :     g_object_unref (parser);
    1062              : 
    1063            6 :     return ata_data;
    1064              : }
    1065              : 
    1066              : 
    1067              : /**
    1068              :  * bd_smart_scsi_get_info:
    1069              :  * @device: device to check.
    1070              :  * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
    1071              :  * @error: (out) (optional): place to store error (if any).
    1072              :  *
    1073              :  * Retrieve SMART information from SCSI or SAS-compliant drive.
    1074              :  *
    1075              :  * Returns: (transfer full): SCSI SMART log or %NULL in case of an error (with @error set).
    1076              :  *
    1077              :  * Tech category: %BD_SMART_TECH_SCSI-%BD_SMART_TECH_MODE_INFO
    1078              :  */
    1079           18 : BDSmartSCSI * bd_smart_scsi_get_info (const gchar *device, const BDExtraArg **extra, GError **error) {
    1080           18 :     const gchar *args[9] = { "smartctl", "--info", "--health", "--attributes", "--log=error", "--log=background", "--json", device, NULL };
    1081           18 :     gint status = 0;
    1082           18 :     gchar *stdout = NULL;
    1083           18 :     gchar *stderr = NULL;
    1084              :     JsonParser *parser;
    1085           18 :     BDSmartSCSI *data = NULL;
    1086              :     gboolean ret;
    1087              : 
    1088           18 :     if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
    1089            0 :         g_prefix_error (error, "Error getting SCSI SMART info: ");
    1090            0 :         return NULL;
    1091              :     }
    1092              : 
    1093           18 :     if (stdout)
    1094           18 :         g_strstrip (stdout);
    1095           18 :     if (stderr)
    1096           18 :         g_strstrip (stderr);
    1097              : 
    1098           18 :     parser = json_parser_new ();
    1099           18 :     ret = parse_smartctl_error (status, stdout, stderr, parser, error);
    1100           18 :     g_free (stdout);
    1101           18 :     g_free (stderr);
    1102           18 :     if (! ret) {
    1103           12 :         g_prefix_error (error, "Error getting SCSI SMART info: ");
    1104           12 :         g_object_unref (parser);
    1105           12 :         return NULL;
    1106              :     }
    1107              : 
    1108            6 :     data = parse_scsi_smart (parser, error);
    1109            6 :     g_object_unref (parser);
    1110              : 
    1111            6 :     return data;
    1112              : }
    1113              : 
    1114              : 
    1115              : /**
    1116              :  * bd_smart_set_enabled:
    1117              :  * @device: SMART-capable device.
    1118              :  * @enabled: whether to enable or disable the SMART functionality
    1119              :  * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
    1120              :  * @error: (out) (optional): place to store error (if any).
    1121              :  *
    1122              :  * Enables or disables SMART functionality on device.
    1123              :  *
    1124              :  * Returns: %TRUE when the functionality was set successfully or %FALSE in case of an error (with @error set).
    1125              :  *
    1126              :  * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
    1127              :  */
    1128           22 : gboolean bd_smart_set_enabled (const gchar *device, gboolean enabled, const BDExtraArg **extra, GError **error) {
    1129           22 :     const gchar *args[5] = { "smartctl", "--json", "--smart=on", device, NULL };
    1130           22 :     gint status = 0;
    1131           22 :     gchar *stdout = NULL;
    1132           22 :     gchar *stderr = NULL;
    1133              :     JsonParser *parser;
    1134              :     gboolean ret;
    1135              : 
    1136           22 :     if (!enabled)
    1137           11 :         args[2] = "--smart=off";
    1138              : 
    1139           22 :     if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
    1140            0 :         g_prefix_error (error, "Error setting SMART functionality: ");
    1141            0 :         return FALSE;
    1142              :     }
    1143              : 
    1144           22 :     if (stdout)
    1145           22 :         g_strstrip (stdout);
    1146           22 :     if (stderr)
    1147           22 :         g_strstrip (stderr);
    1148              : 
    1149           22 :     parser = json_parser_new ();
    1150           22 :     ret = parse_smartctl_error (status, stdout, stderr, parser, error);
    1151           22 :     g_free (stdout);
    1152           22 :     g_free (stderr);
    1153           22 :     g_object_unref (parser);
    1154           22 :     if (! ret) {
    1155           16 :         g_prefix_error (error, "Error setting SMART functionality: ");
    1156           16 :         return FALSE;
    1157              :     }
    1158              : 
    1159            6 :     return TRUE;
    1160              : }
    1161              : 
    1162              : /**
    1163              :  * bd_smart_device_self_test:
    1164              :  * @device: device to trigger the test on.
    1165              :  * @operation: #BDSmartSelfTestOp self-test operation.
    1166              :  * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
    1167              :  * @error: (out) (optional): place to store error (if any).
    1168              :  *
    1169              :  * Executes or aborts device self-test.
    1170              :  *
    1171              :  * Returns: %TRUE when the self-test was triggered successfully or %FALSE in case of an error (with @error set).
    1172              :  *
    1173              :  * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_SELFTEST
    1174              :  */
    1175           58 : gboolean bd_smart_device_self_test (const gchar *device, BDSmartSelfTestOp operation, const BDExtraArg **extra, GError **error) {
    1176           58 :     const gchar *args[5] = { "smartctl", "--json", "--test=", device, NULL };
    1177           58 :     gint status = 0;
    1178           58 :     gchar *stdout = NULL;
    1179           58 :     gchar *stderr = NULL;
    1180              :     JsonParser *parser;
    1181              :     gboolean ret;
    1182              : 
    1183           58 :     switch (operation) {
    1184           12 :         case BD_SMART_SELF_TEST_OP_ABORT:
    1185           12 :             args[2] = "--abort";
    1186           12 :             break;
    1187           12 :         case BD_SMART_SELF_TEST_OP_OFFLINE:
    1188           12 :             args[2] = "--test=offline";
    1189           12 :             break;
    1190           12 :         case BD_SMART_SELF_TEST_OP_SHORT:
    1191           12 :             args[2] = "--test=short";
    1192           12 :             break;
    1193           12 :         case BD_SMART_SELF_TEST_OP_LONG:
    1194           12 :             args[2] = "--test=long";
    1195           12 :             break;
    1196           10 :         case BD_SMART_SELF_TEST_OP_CONVEYANCE:
    1197           10 :             args[2] = "--test=conveyance";
    1198           10 :             break;
    1199            0 :         default:
    1200            0 :             g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
    1201              :                                  "Invalid self-test operation.");
    1202            0 :             return FALSE;
    1203              :     }
    1204              : 
    1205           58 :     if (!bd_utils_exec_and_capture_output_no_progress (args, extra, &stdout, &stderr, &status, error)) {
    1206            0 :         g_prefix_error (error, "Error executing SMART self-test: ");
    1207            0 :         return FALSE;
    1208              :     }
    1209              : 
    1210           58 :     if (stdout)
    1211           58 :         g_strstrip (stdout);
    1212           58 :     if (stderr)
    1213           58 :         g_strstrip (stderr);
    1214              : 
    1215           58 :     parser = json_parser_new ();
    1216           58 :     ret = parse_smartctl_error (status, stdout, stderr, parser, error);
    1217           58 :     g_free (stdout);
    1218           58 :     g_free (stderr);
    1219           58 :     g_object_unref (parser);
    1220           58 :     if (! ret) {
    1221           47 :         g_prefix_error (error, "Error executing SMART self-test: ");
    1222           47 :         return FALSE;
    1223              :     }
    1224              : 
    1225           11 :     return TRUE;
    1226              : }
        

Generated by: LCOV version 2.0-1