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

Generated by: LCOV version 2.0-1