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 : }
|