Line data Source code
1 : /*
2 : * Copyright (C) 2014-2024 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Author: Tomas Bzatek <tbzatek@redhat.com>
18 : */
19 :
20 : #include <glib.h>
21 : #include <string.h>
22 : #include <stdio.h>
23 : #include <unistd.h>
24 : #include <errno.h>
25 :
26 : #include <atasmart.h>
27 :
28 : #include <blockdev/utils.h>
29 : #include <check_deps.h>
30 :
31 : #include "smart.h"
32 : #include "smart-private.h"
33 :
34 : /**
35 : * bd_smart_check_deps:
36 : *
37 : * Returns: whether the plugin's runtime dependencies are satisfied or not
38 : *
39 : * Function checking plugin's runtime dependencies.
40 : *
41 : * Deprecated: 3.5: use %bd_smart_is_tech_avail instead
42 : */
43 0 : gboolean bd_smart_check_deps (void) {
44 0 : return TRUE;
45 : }
46 :
47 : /**
48 : * bd_smart_is_tech_avail:
49 : * @tech: the queried tech
50 : * @mode: a bit mask of queried modes of operation (#BDSmartTechMode) for @tech
51 : * @error: (out) (nullable): place to store error (details about why the @tech-@mode combination is not available)
52 : *
53 : * Returns: whether the @tech-@mode combination is available -- supported by the
54 : * plugin implementation and having all the runtime dependencies available
55 : */
56 2 : gboolean bd_smart_is_tech_avail (BDSmartTech tech, G_GNUC_UNUSED guint64 mode, GError **error) {
57 2 : switch (tech) {
58 1 : case BD_SMART_TECH_ATA:
59 1 : return TRUE;
60 1 : case BD_SMART_TECH_SCSI:
61 1 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "SCSI SMART is unavailable with libatasmart");
62 1 : return FALSE;
63 0 : default:
64 0 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "Unknown technology");
65 0 : return FALSE;
66 : }
67 : }
68 :
69 : /* copied from libatasmart/atasmart.c (a non-public symbol) */
70 248 : static gchar *print_value (uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) {
71 248 : switch (pretty_unit) {
72 18 : case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
73 18 : if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU)
74 5 : return g_strdup_printf ("%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365));
75 13 : if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU)
76 6 : return g_strdup_printf ("%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30));
77 7 : if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU)
78 2 : return g_strdup_printf ("%0.1f days", ((double) pretty_value)/(1000.0*60*60*24));
79 5 : if (pretty_value >= 1000LLU*60LLU*60LLU)
80 0 : return g_strdup_printf ("%0.1f h", ((double) pretty_value)/(1000.0*60*60));
81 5 : if (pretty_value >= 1000LLU*60LLU)
82 0 : return g_strdup_printf ("%0.1f min", ((double) pretty_value)/(1000.0*60));
83 5 : if (pretty_value >= 1000LLU)
84 3 : return g_strdup_printf ("%0.1f s", ((double) pretty_value)/(1000.0));
85 : else
86 2 : return g_strdup_printf ("%llu ms", (unsigned long long) pretty_value);
87 : break;
88 19 : case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
89 19 : return g_strdup_printf ("%0.1f C", ((double) pretty_value - 273150) / 1000);
90 : break;
91 24 : case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
92 24 : return g_strdup_printf ("%llu sectors", (unsigned long long) pretty_value);
93 : break;
94 6 : case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
95 6 : return g_strdup_printf ("%llu%%", (unsigned long long) pretty_value);
96 : break;
97 0 : case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
98 0 : return g_strdup_printf ("%0.3f%%", (double) pretty_value);
99 : break;
100 9 : case SK_SMART_ATTRIBUTE_UNIT_MB:
101 9 : if (pretty_value >= 1000000LLU)
102 2 : return g_strdup_printf ("%0.3f TB", (double) pretty_value / 1000000LLU);
103 7 : if (pretty_value >= 1000LLU)
104 7 : return g_strdup_printf ("%0.3f GB", (double) pretty_value / 1000LLU);
105 : else
106 0 : return g_strdup_printf ("%llu MB", (unsigned long long) pretty_value);
107 : break;
108 106 : case SK_SMART_ATTRIBUTE_UNIT_NONE:
109 106 : return g_strdup_printf ("%llu", (unsigned long long) pretty_value);
110 : break;
111 66 : case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
112 66 : return g_strdup_printf ("n/a");
113 : break;
114 0 : case _SK_SMART_ATTRIBUTE_UNIT_MAX:
115 0 : g_warn_if_reached ();
116 0 : return NULL;
117 : }
118 0 : return NULL;
119 : }
120 :
121 : struct ParseTempData {
122 : guint attr_id;
123 : guint64 value;
124 : DriveDBAttr **drivedb_attrs;
125 : };
126 :
127 265 : static void parse_temp_attr_cb (G_GNUC_UNUSED SkDisk *d,
128 : const SkSmartAttributeParsedData *a,
129 : void *user_data) {
130 265 : struct ParseTempData *data = user_data;
131 :
132 265 : if (a->id != data->attr_id)
133 253 : return;
134 12 : if ((data->attr_id == 194 || data->attr_id == 190) &&
135 12 : a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN)
136 0 : return;
137 : /* TODO: Validate against drivedb when we have backwards mapping
138 : * from smartmontools attribute names back to libatasmart names.
139 : * At this point the well_known_attrs[] table serves only
140 : * for forward validation.
141 : */
142 12 : data->value = a->pretty_value;
143 : }
144 :
145 13 : static guint64 calculate_temperature(SkDisk *d, DriveDBAttr **drivedb_attrs) {
146 13 : const guint temp_attrs[2] = {194, 190, /* 9, 220 */ }; /* as defined in smartmontools/atacmds.cpp:ata_return_temperature_value() */
147 : unsigned long int i;
148 :
149 15 : for (i = 0; i < G_N_ELEMENTS (temp_attrs); i++) {
150 14 : struct ParseTempData parse_data = {
151 14 : .attr_id = temp_attrs[i],
152 : .drivedb_attrs = drivedb_attrs,
153 : .value = 0,
154 : };
155 :
156 14 : if (sk_disk_smart_parse_attributes (d, parse_temp_attr_cb, &parse_data) != 0)
157 0 : break;
158 14 : if (parse_data.value != 0)
159 12 : return parse_data.value;
160 : }
161 1 : return 0;
162 : }
163 :
164 : struct ParseData {
165 : GPtrArray *ptr_array;
166 : const SkIdentifyParsedData *identify_data;
167 : DriveDBAttr **drivedb_attrs;
168 : };
169 :
170 248 : static void parse_attr_cb (G_GNUC_UNUSED SkDisk *d,
171 : const SkSmartAttributeParsedData *a,
172 : void *user_data) {
173 248 : struct ParseData *data = user_data;
174 : BDSmartATAAttribute *attr;
175 :
176 248 : attr = g_new0 (BDSmartATAAttribute, 1);
177 248 : attr->id = a->id;
178 248 : attr->name = g_strdup (a->name);
179 248 : attr->well_known_name = g_strdup (a->name);
180 248 : attr->value = a->current_value_valid ? a->current_value : -1;
181 248 : attr->worst = a->worst_value_valid ? a->worst_value : -1;
182 248 : attr->threshold = a->threshold_valid ? a->threshold : -1;
183 248 : attr->failed_past = attr->worst > 0 && attr->threshold > 0 && attr->worst <= attr->threshold;
184 248 : attr->failing_now = attr->value > 0 && attr->threshold > 0 && attr->value <= attr->threshold;
185 248 : attr->value_raw = ((uint64_t) a->raw[0]) |
186 248 : (((uint64_t) a->raw[1]) << 8) |
187 248 : (((uint64_t) a->raw[2]) << 16) |
188 248 : (((uint64_t) a->raw[3]) << 24) |
189 248 : (((uint64_t) a->raw[4]) << 32) |
190 248 : (((uint64_t) a->raw[5]) << 40);
191 248 : attr->flags = a->flags;
192 248 : attr->pretty_value = a->pretty_value;
193 248 : switch (a->pretty_unit) {
194 66 : case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
195 66 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN;
196 66 : break;
197 106 : case SK_SMART_ATTRIBUTE_UNIT_NONE:
198 106 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_NONE;
199 106 : break;
200 18 : case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
201 18 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_MSECONDS;
202 18 : break;
203 24 : case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
204 24 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_SECTORS;
205 24 : break;
206 19 : case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
207 19 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_MKELVIN;
208 19 : break;
209 0 : case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT:
210 0 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_SMALL_PERCENT;
211 0 : break;
212 6 : case SK_SMART_ATTRIBUTE_UNIT_PERCENT:
213 6 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_PERCENT;
214 6 : break;
215 9 : case SK_SMART_ATTRIBUTE_UNIT_MB:
216 9 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_MB;
217 9 : break;
218 0 : default:
219 0 : attr->pretty_value_unit = BD_SMART_ATA_ATTRIBUTE_UNIT_UNKNOWN;
220 0 : g_warn_if_reached ();
221 : }
222 248 : attr->pretty_value_string = print_value (a->pretty_value, a->pretty_unit);
223 :
224 : /* validate against drivedb */
225 248 : if (data->drivedb_attrs) {
226 : DriveDBAttr **l;
227 :
228 2442 : for (l = data->drivedb_attrs; *l; l++) {
229 2368 : if ((*l)->id == a->id && well_known_attrs[a->id].libatasmart_name) {
230 : const gchar * const *n;
231 28 : gboolean match = FALSE;
232 :
233 64 : for (n = well_known_attrs[a->id].smartmontools_names; *n; n++)
234 40 : if (g_ascii_strcasecmp ((*l)->name, *n) == 0) {
235 4 : match = TRUE;
236 4 : break;
237 : }
238 :
239 28 : if (!match) {
240 24 : g_free (attr->well_known_name);
241 24 : attr->well_known_name = NULL;
242 24 : bd_utils_log_format (BD_UTILS_LOG_DEBUG,
243 : "libatasmart: drive %s: attribute [%d] \"%s\"/\"%s\" not whitelisted, reporting unknown attr",
244 24 : data->identify_data->model, a->id, a->name, (*l)->name);
245 : }
246 : }
247 : }
248 : }
249 :
250 248 : g_ptr_array_add (data->ptr_array, attr);
251 248 : }
252 :
253 16 : static BDSmartATA * parse_sk_data (SkDisk *d, GError **error) {
254 16 : SkBool good = FALSE;
255 16 : SkBool available = FALSE;
256 16 : uint64_t power_on_msec = 0;
257 : const SkSmartParsedData *parsed_data;
258 : BDSmartATA *data;
259 : GPtrArray *ptr_array;
260 16 : struct ParseData parse_data = {0,};
261 :
262 16 : if (sk_disk_smart_read_data (d) != 0) {
263 3 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
264 : "Error reading SMART data from device: %s",
265 3 : strerror_l (errno, _C_LOCALE));
266 3 : return NULL;
267 : }
268 :
269 13 : if (sk_disk_smart_status (d, &good) != 0) {
270 0 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
271 : "Error checking SMART data status: %s",
272 0 : strerror_l (errno, _C_LOCALE));
273 0 : return NULL;
274 : }
275 :
276 13 : if (sk_disk_smart_parse (d, &parsed_data) != 0) {
277 0 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
278 : "Error parsing SMART data: %s",
279 0 : strerror_l (errno, _C_LOCALE));
280 0 : return NULL;
281 : }
282 :
283 13 : data = g_new0 (BDSmartATA, 1);
284 :
285 13 : sk_disk_smart_is_available (d, &available);
286 13 : data->smart_supported = available;
287 : /* At this point when SMART is not and cannot be enabled,
288 : * sk_disk_smart_read_data() would've already returned an error.
289 : */
290 13 : data->smart_enabled = TRUE;
291 13 : data->overall_status_passed = good;
292 :
293 13 : switch (parsed_data->offline_data_collection_status) {
294 8 : case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER:
295 8 : data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NEVER_STARTED;
296 8 : break;
297 3 : case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS:
298 3 : data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_NO_ERROR;
299 3 : break;
300 0 : case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS:
301 0 : data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_IN_PROGRESS;
302 0 : break;
303 2 : case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED:
304 2 : data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED_INTR;
305 2 : break;
306 0 : case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED:
307 0 : data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_INTR;
308 0 : break;
309 0 : case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL:
310 0 : data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_ABORTED_ERROR;
311 0 : break;
312 0 : case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN:
313 0 : data->offline_data_collection_status = BD_SMART_ATA_OFFLINE_DATA_COLLECTION_STATUS_VENDOR_SPECIFIC;
314 0 : break;
315 0 : default:
316 0 : g_warn_if_reached ();
317 : }
318 :
319 13 : data->auto_offline_data_collection_enabled = FALSE; /* TODO */
320 13 : data->offline_data_collection_completion = parsed_data->total_offline_data_collection_seconds;
321 13 : data->offline_data_collection_capabilities = 0; /* TODO */
322 :
323 13 : switch (parsed_data->self_test_execution_status) {
324 9 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER:
325 9 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_COMPLETED_NO_ERROR;
326 9 : break;
327 2 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED:
328 2 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ABORTED_HOST;
329 2 : break;
330 0 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED:
331 0 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_INTR_HOST_RESET;
332 0 : break;
333 0 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL:
334 0 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_FATAL;
335 0 : break;
336 0 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN:
337 0 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_UNKNOWN;
338 0 : break;
339 0 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL:
340 0 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_ELECTRICAL;
341 0 : break;
342 0 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO:
343 0 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_SERVO;
344 0 : break;
345 2 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ:
346 2 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_READ;
347 2 : break;
348 0 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING:
349 0 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_ERROR_HANDLING;
350 0 : break;
351 0 : case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS:
352 0 : data->self_test_status = BD_SMART_ATA_SELF_TEST_STATUS_IN_PROGRESS;
353 0 : break;
354 0 : default:
355 0 : g_warn_if_reached ();
356 : }
357 :
358 13 : data->self_test_percent_remaining = parsed_data->self_test_execution_percent_remaining;
359 13 : data->self_test_polling_short = parsed_data->short_test_polling_minutes;
360 13 : data->self_test_polling_extended = parsed_data->extended_test_polling_minutes;
361 13 : data->self_test_polling_conveyance = parsed_data->conveyance_test_polling_minutes;
362 :
363 13 : data->smart_capabilities = 0; /* TODO */
364 :
365 13 : sk_disk_smart_get_power_on (d, &power_on_msec);
366 13 : data->power_on_time = power_on_msec / 1000 / 60;
367 :
368 13 : sk_disk_smart_get_power_cycle (d, &data->power_cycle_count);
369 :
370 13 : ptr_array = g_ptr_array_new_full (0, (GDestroyNotify) bd_smart_ata_attribute_free);
371 13 : parse_data.ptr_array = ptr_array;
372 : #ifdef HAVE_DRIVEDB_H
373 13 : sk_disk_identify_parse (d, &parse_data.identify_data);
374 13 : if (parse_data.identify_data)
375 13 : parse_data.drivedb_attrs = drivedb_lookup_drive (parse_data.identify_data->model,
376 13 : parse_data.identify_data->firmware,
377 : FALSE /* include_defaults */);
378 : #endif
379 13 : data->temperature = calculate_temperature (d, parse_data.drivedb_attrs) / 1000;
380 :
381 13 : if (sk_disk_smart_parse_attributes (d, parse_attr_cb, &parse_data) != 0) {
382 0 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
383 : "Error parsing SMART data: %s",
384 0 : strerror_l (errno, _C_LOCALE));
385 0 : g_ptr_array_free (ptr_array, TRUE);
386 0 : bd_smart_ata_free (data);
387 0 : return NULL;
388 : }
389 13 : free_drivedb_attrs (parse_data.drivedb_attrs);
390 13 : g_ptr_array_add (ptr_array, NULL);
391 13 : data->attributes = (BDSmartATAAttribute **) g_ptr_array_free (ptr_array, FALSE);
392 :
393 13 : return data;
394 : }
395 :
396 : /**
397 : * bd_smart_ata_get_info:
398 : * @device: device to check.
399 : * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
400 : * @error: (out) (optional): place to store error (if any).
401 : *
402 : * Retrieve SMART information from the drive.
403 : *
404 : * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
405 : *
406 : * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
407 : */
408 4 : BDSmartATA * bd_smart_ata_get_info (const gchar *device, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
409 : SkDisk *d;
410 : BDSmartATA *data;
411 :
412 4 : g_warn_if_fail (device != NULL);
413 :
414 4 : if (sk_disk_open (device, &d) != 0) {
415 1 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
416 : "Error opening device %s: %s",
417 : device,
418 1 : strerror_l (errno, _C_LOCALE));
419 1 : return NULL;
420 : }
421 :
422 3 : data = parse_sk_data (d, error);
423 3 : sk_disk_free (d);
424 :
425 3 : return data;
426 : }
427 :
428 :
429 : /**
430 : * bd_smart_ata_get_info_from_data:
431 : * @data: (array length=data_len): binary data to parse.
432 : * @data_len: length of the data supplied.
433 : * @error: (out) (optional): place to store error (if any).
434 : *
435 : * Retrieve SMART information from the supplied data.
436 : *
437 : * Returns: (transfer full): ATA SMART log or %NULL in case of an error (with @error set).
438 : *
439 : * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
440 : */
441 15 : BDSmartATA * bd_smart_ata_get_info_from_data (const guint8 *data, gsize data_len, GError **error) {
442 : SkDisk *d;
443 : BDSmartATA *ata_data;
444 :
445 15 : g_warn_if_fail (data != NULL);
446 15 : g_warn_if_fail (data_len > 0);
447 :
448 15 : if (sk_disk_open (NULL, &d) != 0) {
449 0 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
450 : "Error parsing blob data: %s",
451 0 : strerror_l (errno, _C_LOCALE));
452 0 : return NULL;
453 : }
454 :
455 15 : if (sk_disk_set_blob (d, data, data_len) != 0) {
456 2 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
457 : "Error parsing blob data: %s",
458 2 : strerror_l (errno, _C_LOCALE));
459 2 : return NULL;
460 : }
461 :
462 13 : ata_data = parse_sk_data (d, error);
463 13 : sk_disk_free (d);
464 :
465 13 : return ata_data;
466 : }
467 :
468 :
469 : /**
470 : * bd_smart_scsi_get_info:
471 : * @device: device to check.
472 : * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
473 : * @error: (out) (optional): place to store error (if any).
474 : *
475 : * Retrieve SMART information from SCSI or SAS-compliant drive.
476 : *
477 : * Returns: (transfer full): SCSI SMART log or %NULL in case of an error (with @error set).
478 : *
479 : * Tech category: %BD_SMART_TECH_SCSI-%BD_SMART_TECH_MODE_INFO
480 : */
481 4 : BDSmartSCSI * bd_smart_scsi_get_info (G_GNUC_UNUSED const gchar *device, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
482 4 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "SCSI SMART is unavailable with libatasmart");
483 4 : return FALSE;
484 : }
485 :
486 :
487 : /**
488 : * bd_smart_set_enabled:
489 : * @device: SMART-capable device.
490 : * @enabled: whether to enable or disable the SMART functionality
491 : * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
492 : * @error: (out) (optional): place to store error (if any).
493 : *
494 : * Enables or disables SMART functionality on device.
495 : *
496 : * Returns: %TRUE when the functionality was set successfully or %FALSE in case of an error (with @error set).
497 : *
498 : * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_INFO
499 : */
500 8 : gboolean bd_smart_set_enabled (G_GNUC_UNUSED const gchar *device, G_GNUC_UNUSED gboolean enabled, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
501 8 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_TECH_UNAVAIL, "Enabling/disabling ATA SMART functionality is unavailable with libatasmart");
502 8 : return FALSE;
503 : }
504 :
505 :
506 : /**
507 : * bd_smart_device_self_test:
508 : * @device: device to trigger the test on.
509 : * @operation: #BDSmartSelfTestOp self-test operation.
510 : * @extra: (nullable) (array zero-terminated=1): extra options to pass through.
511 : * @error: (out) (optional): place to store error (if any).
512 : *
513 : * Executes or aborts device self-test.
514 : *
515 : * Returns: %TRUE when the self-test was triggered successfully or %FALSE in case of an error (with @error set).
516 : *
517 : * Tech category: %BD_SMART_TECH_ATA-%BD_SMART_TECH_MODE_SELFTEST
518 : */
519 20 : gboolean bd_smart_device_self_test (const gchar *device, BDSmartSelfTestOp operation, G_GNUC_UNUSED const BDExtraArg **extra, GError **error) {
520 : SkDisk *d;
521 : SkSmartSelfTest op;
522 : gboolean ret;
523 :
524 20 : switch (operation) {
525 4 : case BD_SMART_SELF_TEST_OP_ABORT:
526 4 : op = SK_SMART_SELF_TEST_ABORT;
527 4 : break;
528 4 : case BD_SMART_SELF_TEST_OP_SHORT:
529 4 : op = SK_SMART_SELF_TEST_SHORT;
530 4 : break;
531 8 : case BD_SMART_SELF_TEST_OP_LONG:
532 : case BD_SMART_SELF_TEST_OP_OFFLINE:
533 8 : op = SK_SMART_SELF_TEST_EXTENDED;
534 8 : break;
535 4 : case BD_SMART_SELF_TEST_OP_CONVEYANCE:
536 4 : op = SK_SMART_SELF_TEST_CONVEYANCE;
537 4 : break;
538 0 : default:
539 0 : g_set_error_literal (error, BD_SMART_ERROR, BD_SMART_ERROR_INVALID_ARGUMENT,
540 : "Invalid self-test operation.");
541 0 : return FALSE;
542 : }
543 :
544 20 : if (sk_disk_open (device, &d) != 0) {
545 5 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
546 : "Error opening device %s: %s",
547 : device,
548 5 : strerror_l (errno, _C_LOCALE));
549 5 : return FALSE;
550 : }
551 :
552 15 : ret = sk_disk_smart_self_test (d, op) == 0;
553 15 : sk_disk_free (d);
554 15 : if (!ret) {
555 15 : g_set_error (error, BD_SMART_ERROR, BD_SMART_ERROR_FAILED,
556 : "Error triggering device self-test: %s",
557 15 : strerror_l (errno, _C_LOCALE));
558 15 : return FALSE;
559 : }
560 :
561 0 : return TRUE;
562 : }
|