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