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