From 2713325d003f087969f61532d933597f1bed2dfe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 1 Jul 2008 21:22:18 +0200 Subject: remove stuff that moved to libatasmart --- Makefile | 17 +- libatasmart.vapi | 140 ++++++ skdump.c | 54 --- sktest.c | 69 --- smart.c | 1258 ------------------------------------------------------ smart.h | 173 -------- smart.vapi | 140 ------ 7 files changed, 144 insertions(+), 1707 deletions(-) create mode 100644 libatasmart.vapi delete mode 100644 skdump.c delete mode 100644 sktest.c delete mode 100644 smart.c delete mode 100644 smart.h delete mode 100644 smart.vapi diff --git a/Makefile b/Makefile index 1e4b1d5..d350dec 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,7 @@ -CFLAGS=-pipe -Wall -W -O0 -g -I. -LIBS= +all: smartkitd gnome-disk-health gnome-disk-health.ui -all: skdump sktest smartkitd gnome-disk-health gnome-disk-health.ui - -skdump: smart.o skdump.o - $(CC) -o $@ $^ $(CFLAGS) $(LIBS) - -sktest: smart.o sktest.o - $(CC) -o $@ $^ $(CFLAGS) $(LIBS) - -smartkitd: smart.c smartkitd.vala - valac --save-temps -g -o $@ --vapidir=. --pkg=smart --pkg=hal --pkg=dbus-glib-1 --Xcc=-I. $^ +smartkitd: smartkitd.vala + valac --save-temps -g -o $@ --vapidir=. --pkg=libatasmart --pkg=hal --pkg=dbus-glib-1 --Xcc="$(shell pkg-config --cflags --libs libatasmart)" $^ gnome-disk-health: gnome-disk-health.vala valac --save-temps -g -o $@ --pkg=gtk+-2.0 --pkg=dbus-glib-1 $^ @@ -19,4 +10,4 @@ gnome-disk-health.ui: gnome-disk-health.glade gtk-builder-convert $< $@ clean: - rm -f skdump sktest *.o smartkitd gnome-disk-health gnome-disk-health.ui + rm -f *.o smartkitd gnome-disk-health gnome-disk-health.ui diff --git a/libatasmart.vapi b/libatasmart.vapi new file mode 100644 index 0000000..3acb02d --- /dev/null +++ b/libatasmart.vapi @@ -0,0 +1,140 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of SmartKit. + + Copyright 2008 Lennart Poettering + + SmartKit is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + SmartKit is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with SmartKit. If not, If not, see + . +***/ + +using GLib; + +[CCode (cheader_filename="atasmart.h")] +namespace Smart { + + [CCode (cname="SkSmartSelfTest", cprefix="SK_SMART_SELF_TEST_")] + public enum SmartSelfTest { + SHORT, EXTENDED, CONVEYANCE, ABORT + } + + [Immutable] + [CCode (cname="SkIdentifyParsedData")] + public struct IdentifyParsedData { + public string serial; + public string firmware; + public string model; + } + + [CCode (cname="SkSmartOfflineDataCollectionStatus", cprefix="SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_")] + public enum SmartOfflineDataCollectionStatus { + NEVER, SUCCESS, INPROGRESS, SUSPENDED, ABORTED, FATAL, UNKNOWN + } + + [CCode (cname="sk_smart_offline_data_collection_status_to_string")] + public weak string smart_offline_data_collection_status_to_string(SmartOfflineDataCollectionStatus status); + + + [CCode (cname="SkSmartSelfTestExecutionStatus", cprefix="SK_SMART_SELF_TEST_EXECUTION_STATUS_")] + public enum SmartSelfTestExecutionStatus { + SUCCESS_OR_NEVER, ABORTED, INTERRUPTED, FATAL, ERROR_UNKNOWN, ERROR_ELECTRICAL, ERROR_SERVO, ERROR_READ, ERROR_HANDLING, INPROGRESS + } + + [CCode (cname="sk_smart_self_test_execution_status_to_string")] + public weak string smart_self_test_execution_status_to_string(SmartSelfTestExecutionStatus status); + + [Immutable] + [CCode (cname="SkSmartParsedData")] + public struct SmartParsedData { + public SmartOfflineDataCollectionStatus offline_data_collection_status; + public uint total_offline_data_collection_seconds; + public SmartSelfTestExecutionStatus self_test_execution_status; + public uint self_test_execution_percent_remaining; + + public bool conveyance_test_available; + public bool short_and_extended_test_available; + public bool start_test_available; + public bool abort_test_available; + + public uint short_test_polling_minutes; + public uint extended_test_polling_minutes; + public uint conveyance_test_polling_minutes; + + [CCode (cname="sk_smart_self_test_available")] + public bool self_test_available(SmartSelfTest test); + + [CCode (cname="sk_smart_self_test_polling_minutes")] + public uint self_test_polling_minutes(SmartSelfTest test); + } + + [CCode (cname="SkSmartAttributeUnit", cprefix="SK_SMART_ATTRIBUTE_UNIT_")] + public enum SmartAttributeUnit { + UNKNOWN, NONE, MSECONDS, SECTORS, MKELVIN + } + + [CCode (cname="sk_smart_attribute_unit_to_string")] + public weak string smart_attribute_unit_to_string(SmartAttributeUnit unit); + + [Immutable] + [CCode (cname="SkSmartAttributeParsedData")] + public struct SmartAttributeParsedData { + public uint8 id; + public string name; + public SmartAttributeUnit pretty_unit; + public uint16 flags; + public uint8 threshold; + public bool threshold_valid; + public bool online; + public bool prefailure; + public bool good; + public uint8 current_value; + public uint8 worst_value; + public uint64 pretty_value; + public uint8[6] raw; + } + + [CCode (cname="SkSmartAttributeParseCallback")] + public delegate void SmartAttributeParseCallback(void* disk, SmartAttributeParsedData a); + + [Compact] + [CCode (free_function="sk_disk_free", cname="SkDisk", cprefix="sk_disk_")] + public class Disk { + + public static int open(string name, out Disk disk); + + public int get_size(out uint64 bytes); + + public int check_sleep_mode(out bool awake); + + public int identify_is_available(out bool available); + public int identify_parse(out weak IdentifyParsedData* data); + + public int smart_is_available(out bool available); + public int smart_status(out bool good); + public int smart_read_data(); + public int smart_parse_attributes(SmartAttributeParseCallback cb); + public int smart_parse(out weak SmartParsedData* data); + public int smart_self_test(SmartSelfTest test); + + public int dump(); + } + + /* These two should move to an official vala package */ + [CCode (cname="errno", cheader_filename="errno.h")] + public int errno; + + [CCode (cname="g_strerror", cheader_filename="glib.h")] + public weak string strerror(int err); +} diff --git a/skdump.c b/skdump.c deleted file mode 100644 index 93bf18f..0000000 --- a/skdump.c +++ /dev/null @@ -1,54 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ - -/*** - This file is part of SmartKit. - - Copyright 2008 Lennart Poettering - - SmartKit is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - SmartKit is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with SmartKit. If not, If not, see - . -***/ - -#include -#include -#include - -#include "smart.h" - -int main(int argc, char *argv[]) { - int ret; - const char *device; - SkDisk *d; - - if (argc != 2) { - fprintf(stderr, "%s [DEVICE]\n", argv[0]); - return 1; - } - - device = argv[1]; - - if ((ret = sk_disk_open(device, &d)) < 0) { - fprintf(stderr, "Failed to open disk %s: %s\n", device, strerror(errno)); - return 1; - } - - if ((ret = sk_disk_dump(d)) < 0) { - fprintf(stderr, "Failed to dump disk data: %s\n", strerror(errno)); - return 1; - } - - sk_disk_free(d); - - return 0; -} diff --git a/sktest.c b/sktest.c deleted file mode 100644 index 440df81..0000000 --- a/sktest.c +++ /dev/null @@ -1,69 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ - -/*** - This file is part of SmartKit. - - Copyright 2008 Lennart Poettering - - SmartKit is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - SmartKit is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with SmartKit. If not, If not, see - . -***/ - -#include -#include -#include - -#include "smart.h" - -int main(int argc, char *argv[]) { - int ret; - const char *device; - SkDisk *d; - SkSmartSelfTest test; - - if (argc < 3) { - fprintf(stderr, "%s [DEVICE] [short|extended|conveyance|abort]\n", argv[0]); - return 1; - } - - device = argv[1]; - - if (!strcasecmp(argv[2], sk_smart_self_test_to_string(SK_SMART_SELF_TEST_SHORT))) - test = SK_SMART_SELF_TEST_SHORT; - else if (!strcasecmp(argv[2], sk_smart_self_test_to_string(SK_SMART_SELF_TEST_EXTENDED))) - test = SK_SMART_SELF_TEST_EXTENDED; - else if (!(strcasecmp(argv[2], sk_smart_self_test_to_string(SK_SMART_SELF_TEST_CONVEYANCE)))) - test = SK_SMART_SELF_TEST_CONVEYANCE; - else if (!(strcasecmp(argv[2], sk_smart_self_test_to_string(SK_SMART_SELF_TEST_ABORT)))) - test = SK_SMART_SELF_TEST_ABORT; - else { - fprintf(stderr, "Unknown test '%s'.\n", argv[2]); - return 1; - } - - if ((ret = sk_disk_open(device, &d)) < 0) { - fprintf(stderr, "Failed to open disk %s: %s\n", device, strerror(errno)); - return 1; - } - - if ((ret = sk_disk_smart_self_test(d, test)) < 0) { - fprintf(stderr, "Failed to start sel-test: %s\n", strerror(errno)); - return 1; - - } - - sk_disk_free(d); - - return 0; -} diff --git a/smart.c b/smart.c deleted file mode 100644 index 9d8a530..0000000 --- a/smart.c +++ /dev/null @@ -1,1258 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ - -/*** - This file is part of SmartKit. - - Copyright 2008 Lennart Poettering - - SmartKit is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - SmartKit is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with SmartKit. If not, If not, see - . -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "smart.h" - -#define SK_TIMEOUT 2000 - -typedef enum SkDirection { - SK_DIRECTION_NONE, - SK_DIRECTION_IN, - SK_DIRECTION_OUT, - _SK_DIRECTION_MAX -} SkDirection; - -typedef enum SkDiskType { - SK_DISK_TYPE_ATA_PASSTHROUGH, /* ATA passthrough over SCSI transport */ - SK_DISK_TYPE_ATA, - SK_DISK_TYPE_UNKNOWN, - _SK_DISK_TYPE_MAX -} SkDiskType; - -struct SkDisk { - char *name; - int fd; - SkDiskType type; - - uint64_t size; - - uint8_t identify[512]; - uint8_t smart_data[512]; - uint8_t smart_threshold_data[512]; - - SkBool identify_data_valid:1; - SkBool smart_data_valid:1; - SkBool smart_threshold_data_valid:1; - - SkIdentifyParsedData identify_parsed_data; - SkSmartParsedData smart_parsed_data; -}; - -/* ATA commands */ -typedef enum SkAtaCommand { - SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC, - SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1, - SK_ATA_COMMAND_SMART = 0xB0, - SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5 -} SkAtaCommand; - -/* ATA SMART subcommands (ATA8 7.52.1) */ -typedef enum SkSmartCommand { - SK_SMART_COMMAND_READ_DATA = 0xD0, - SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1, - SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4, - SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8, - SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9, - SK_SMART_COMMAND_RETURN_STATUS = 0xDA -} SkSmartCommand; - -static SkBool disk_smart_is_available(SkDisk *d) { - return d->identify_data_valid && !!(d->identify[164] & 1); -} - -static SkBool disk_smart_is_enabled(SkDisk *d) { - return d->identify_data_valid && !!(d->identify[170] & 1); -} - -static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) { - assert(d->smart_data_valid); - - return !!(d->smart_data[367] & 32); -} -static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) { - assert(d->smart_data_valid); - - return !!(d->smart_data[367] & 16); -} - -static SkBool disk_smart_is_start_test_available(SkDisk *d) { - assert(d->smart_data_valid); - - return !!(d->smart_data[367] & 1); -} - -static SkBool disk_smart_is_abort_test_available(SkDisk *d) { - assert(d->smart_data_valid); - - return !!(d->smart_data[367] & 41); -} - -static int disk_ata_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { - uint8_t *bytes = cmd_data; - int ret; - - assert(d->type == SK_DISK_TYPE_ATA); - - switch (direction) { - - case SK_DIRECTION_OUT: - - /* We could use HDIO_DRIVE_TASKFILE here, but - * that's a deprecated ioctl(), hence we don't - * do it. And we don't need writing anyway. */ - - errno = ENOTSUP; - return -1; - - case SK_DIRECTION_IN: { - uint8_t *ioctl_data; - - /* We have HDIO_DRIVE_CMD which can only read, but not write, - * and cannot do LBA. We use it for all read commands. */ - - ioctl_data = alloca(4 + *len); - memset(ioctl_data, 0, 4 + *len); - - ioctl_data[0] = (uint8_t) command; /* COMMAND */ - ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */ - ioctl_data[2] = bytes[1]; /* FEATURE */ - ioctl_data[3] = bytes[3]; /* NSECTOR */ - - if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0) - return ret; - - memset(bytes, 0, 12); - bytes[11] = ioctl_data[0]; - bytes[1] = ioctl_data[1]; - bytes[3] = ioctl_data[2]; - - memcpy(data, ioctl_data+4, *len); - - return ret; - } - - case SK_DIRECTION_NONE: { - uint8_t ioctl_data[7]; - - /* We have HDIO_DRIVE_TASK which can neither read nor - * write, but can do LBA. We use it for all commands that - * do neither read nor write */ - - memset(ioctl_data, 0, sizeof(ioctl_data)); - - ioctl_data[0] = (uint8_t) command; /* COMMAND */ - ioctl_data[1] = bytes[1]; /* FEATURE */ - ioctl_data[2] = bytes[3]; /* NSECTOR */ - - ioctl_data[3] = bytes[9]; /* LBA LOW */ - ioctl_data[4] = bytes[8]; /* LBA MID */ - ioctl_data[5] = bytes[7]; /* LBA HIGH */ - ioctl_data[6] = bytes[10]; /* SELECT */ - - if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data))) - return ret; - - memset(bytes, 0, 12); - bytes[11] = ioctl_data[0]; - bytes[1] = ioctl_data[1]; - bytes[3] = ioctl_data[2]; - - bytes[9] = ioctl_data[3]; - bytes[8] = ioctl_data[4]; - bytes[7] = ioctl_data[5]; - - bytes[10] = ioctl_data[6]; - - return ret; - } - - default: - assert(FALSE); - return -1; - } -} - -/* Sends a SCSI command block */ -static int sg_io(int fd, int direction, - const void *cdb, size_t cdb_len, - void *data, size_t data_len, - void *sense, size_t sense_len) { - - struct sg_io_hdr io_hdr; - - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - - io_hdr.interface_id = 'S'; - io_hdr.cmdp = (unsigned char*) cdb; - io_hdr.cmd_len = cdb_len; - io_hdr.dxferp = data; - io_hdr.dxfer_len = data_len; - io_hdr.sbp = sense; - io_hdr.mx_sb_len = sense_len; - io_hdr.dxfer_direction = direction; - io_hdr.timeout = SK_TIMEOUT; - - return ioctl(fd, SG_IO, &io_hdr); -} - -static int disk_passthrough_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { - uint8_t *bytes = cmd_data; - uint8_t cdb[16]; - uint8_t sense[32]; - uint8_t *desc = sense+8; - int ret; - - static const int direction_map[] = { - [SK_DIRECTION_NONE] = SG_DXFER_NONE, - [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV, - [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV - }; - - assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH); - - /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8 - * ATA Command Pass-Through": - * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */ - - memset(cdb, 0, sizeof(cdb)); - - cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */ - - if (direction == SK_DIRECTION_NONE) { - cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */ - cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */ - - } else if (direction == SK_DIRECTION_IN) { - cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */ - cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ - - } else if (direction == SK_DIRECTION_OUT) { - cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */ - cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */ - } - - cdb[3] = bytes[0]; /* FEATURES */ - cdb[4] = bytes[1]; - - cdb[5] = bytes[2]; /* SECTORS */ - cdb[6] = bytes[3]; - - cdb[8] = bytes[9]; /* LBA LOW */ - cdb[10] = bytes[8]; /* LBA MID */ - cdb[12] = bytes[7]; /* LBA HIGH */ - - cdb[13] = bytes[10] & 0x4F; /* SELECT */ - cdb[14] = (uint8_t) command; - - if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, (size_t) cdb[6] * 512, sense, sizeof(sense))) < 0) - return ret; - - if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) { - errno = EIO; - return -1; - } - - memset(bytes, 0, 12); - - bytes[1] = desc[3]; - bytes[2] = desc[4]; - bytes[3] = desc[5]; - bytes[9] = desc[7]; - bytes[8] = desc[9]; - bytes[7] = desc[11]; - bytes[10] = desc[12]; - bytes[11] = desc[13]; - - return ret; -} - -static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { - - static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = { - [SK_DISK_TYPE_ATA] = disk_ata_command, - [SK_DISK_TYPE_ATA_PASSTHROUGH] = disk_passthrough_command, - }; - - assert(d); - assert(d->type <= _SK_DISK_TYPE_MAX); - assert(direction <= _SK_DIRECTION_MAX); - - assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0)); - assert(direction != SK_DIRECTION_NONE || (!data && !len)); - - return disk_command_table[d->type](d, command, direction, cmd_data, data, len); -} - -static int disk_identify_device(SkDisk *d) { - uint16_t cmd[6]; - int ret; - size_t len = 512; - - memset(cmd, 0, sizeof(cmd)); - - cmd[1] = htons(1); - - if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0) - return ret; - - if (len != 512) { - errno = EIO; - return -1; - } - - d->identify_data_valid = TRUE; - - return 0; -} - -int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) { - int ret; - uint16_t cmd[6]; - - if (!d->identify_data_valid) { - errno = ENOTSUP; - return -1; - } - - memset(cmd, 0, sizeof(cmd)); - - if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0) - return ret; - - if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) { - errno = EIO; - return -1; - } - - *awake = ntohs(cmd[1]) == 0xFF; - - return 0; -} - -static int disk_smart_enable(SkDisk *d, SkBool b) { - uint16_t cmd[6]; - - if (!disk_smart_is_available(d)) { - errno = ENOTSUP; - return -1; - } - - memset(cmd, 0, sizeof(cmd)); - - cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS); - cmd[2] = htons(0x0000U); - cmd[3] = htons(0x00C2U); - cmd[4] = htons(0x4F00U); - - return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0); -} - -int sk_disk_smart_read_data(SkDisk *d) { - uint16_t cmd[6]; - int ret; - size_t len = 512; - - if (!disk_smart_is_available(d)) { - errno = ENOTSUP; - return -1; - } - - memset(cmd, 0, sizeof(cmd)); - - cmd[0] = htons(SK_SMART_COMMAND_READ_DATA); - cmd[1] = htons(1); - cmd[2] = htons(0x0000U); - cmd[3] = htons(0x00C2U); - cmd[4] = htons(0x4F00U); - - if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0) - return ret; - - d->smart_data_valid = TRUE; - - return ret; -} - -static int disk_smart_read_thresholds(SkDisk *d) { - uint16_t cmd[6]; - int ret; - size_t len = 512; - - if (!disk_smart_is_available(d)) { - errno = ENOTSUP; - return -1; - } - - memset(cmd, 0, sizeof(cmd)); - - cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS); - cmd[1] = htons(1); - cmd[2] = htons(0x0000U); - cmd[3] = htons(0x00C2U); - cmd[4] = htons(0x4F00U); - - if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_threshold_data, &len)) < 0) - return ret; - - d->smart_threshold_data_valid = TRUE; - - return ret; -} - -int sk_disk_smart_status(SkDisk *d, SkBool *good) { - uint16_t cmd[6]; - int ret; - - if (!disk_smart_is_available(d)) { - errno = ENOTSUP; - return -1; - } - - memset(cmd, 0, sizeof(cmd)); - - cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS); - cmd[1] = htons(0x0000U); - cmd[3] = htons(0x00C2U); - cmd[4] = htons(0x4F00U); - - if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0) - return ret; - - if (cmd[3] == htons(0x00C2U) && - cmd[4] == htons(0x4F00U)) - *good = TRUE; - else if (cmd[3] == htons(0x002CU) && - cmd[4] == htons(0xF400U)) - *good = FALSE; - else { - errno = EIO; - return -1; - } - - return ret; -} - -int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) { - uint16_t cmd[6]; - int ret; - - if (!disk_smart_is_available(d)) { - errno = ENOTSUP; - return -1; - } - - if (!d->smart_data_valid) - if ((ret = sk_disk_smart_read_data(d)) < 0) - return -1; - - assert(d->smart_data_valid); - - if (test != SK_SMART_SELF_TEST_SHORT && - test != SK_SMART_SELF_TEST_EXTENDED && - test != SK_SMART_SELF_TEST_CONVEYANCE && - test != SK_SMART_SELF_TEST_ABORT) { - errno = EINVAL; - return -1; - } - - if (!disk_smart_is_start_test_available(d) - || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d)) - || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d)) - || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) { - errno = ENOTSUP; - return -1; - } - - if (test == SK_SMART_SELF_TEST_ABORT && - !disk_smart_is_abort_test_available(d)) { - errno = ENOTSUP; - return -1; - } - - memset(cmd, 0, sizeof(cmd)); - - cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE); - cmd[2] = htons(0x0000U); - cmd[3] = htons(0x00C2U); - cmd[4] = htons(0x4F00U | (uint16_t) test); - - return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL); -} - -static void swap_strings(char *s, size_t len) { - assert((len & 1) == 0); - - for (; len > 0; s += 2, len -= 2) { - char t; - t = s[0]; - s[0] = s[1]; - s[1] = t; - } -} - -static void clean_strings(char *s) { - char *e; - - for (e = s; *e; e++) - if (*e < ' ' || *e >= 127) - *e = ' '; -} - -static void drop_spaces(char *s) { - char *d = s; - SkBool prev_space = FALSE; - - s += strspn(s, " "); - - for (;*s; s++) { - - if (prev_space) { - if (*s != ' ') { - prev_space = FALSE; - *(d++) = ' '; - } - } else { - if (*s == ' ') - prev_space = TRUE; - else - *(d++) = *s; - } - } - - *d = 0; -} - -static void read_string(char *d, uint8_t *s, size_t len) { - memcpy(d, s, len); - d[len] = 0; - swap_strings(d, len); - clean_strings(d); - drop_spaces(d); -} - -int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) { - - if (!d->identify_data_valid) { - errno = ENOENT; - return -1; - } - - read_string(d->identify_parsed_data.serial, d->identify+20, 20); - read_string(d->identify_parsed_data.firmware, d->identify+46, 8); - read_string(d->identify_parsed_data.model, d->identify+54, 40); - - *ipd = &d->identify_parsed_data; - - return 0; -} - -int sk_disk_smart_is_available(SkDisk *d, SkBool *b) { - - if (!d->identify_data_valid) { - errno = ENOTSUP; - return -1; - } - - *b = disk_smart_is_available(d); - return 0; -} - -int sk_disk_identify_is_available(SkDisk *d, SkBool *b) { - - *b = d->identify_data_valid; - return 0; -} - -const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) { - - static const char* const map[] = { - [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.", - [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.", - [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.", - [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.", - [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.", - [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.", - [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status" - }; - - if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX) - return NULL; - - return map[status]; -} - -const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) { - - static const char* const map[] = { - [SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER] = "The previous self-test routine completed without error or no self-test has ever been run.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL] = "A fatal error or unknown test error occurred while the device was executing its self-test routine and the device was unable to complete the self-test routine.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN] = "The previous self-test completed having a test element that failed and the test element that failed.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO] = "The previous self-test completed having the servo (and/or seek) test element of the test failed.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING] = "The previous self-test completed having a test element that failed and the device is suspected of having handling damage.", - [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress" - }; - - if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX) - return NULL; - - return map[status]; -} - -const char* sk_smart_self_test_to_string(SkSmartSelfTest test) { - - switch (test) { - case SK_SMART_SELF_TEST_SHORT: - return "short"; - case SK_SMART_SELF_TEST_EXTENDED: - return "extended"; - case SK_SMART_SELF_TEST_CONVEYANCE: - return "conveyance"; - case SK_SMART_SELF_TEST_ABORT: - return "abort"; - } - - return NULL; -} - -SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) { - - if (!d->start_test_available) - return FALSE; - - switch (test) { - case SK_SMART_SELF_TEST_SHORT: - case SK_SMART_SELF_TEST_EXTENDED: - return d->short_and_extended_test_available; - case SK_SMART_SELF_TEST_CONVEYANCE: - return d->conveyance_test_available; - case SK_SMART_SELF_TEST_ABORT: - return d->abort_test_available; - default: - return FALSE; - } -} - -unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) { - - if (!sk_smart_self_test_available(d, test)) - return 0; - - switch (test) { - case SK_SMART_SELF_TEST_SHORT: - return d->short_test_polling_minutes; - case SK_SMART_SELF_TEST_EXTENDED: - return d->extended_test_polling_minutes; - case SK_SMART_SELF_TEST_CONVEYANCE: - return d->conveyance_test_polling_minutes; - default: - return 0; - } -} - -typedef struct SkSmartAttributeInfo { - const char *name; - SkSmartAttributeUnit unit; -} SkSmartAttributeInfo; - -/* This data is stolen from smartmontools */ -static const SkSmartAttributeInfo const attribute_info[255] = { - [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [2] = { "throughput-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS }, - [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [8] = { "seek-time-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS }, - [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN }, - [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN }, - [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS }, - [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS }, - [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [204] = { "shock-count-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [205] = { "shock-rate-write-opern", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN}, - [209] = { "offline-seek-perfomance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS }, - [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [225] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS }, - [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [228] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE }, - [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN }, - [231] = { "temperature-celsius-1", SK_SMART_ATTRIBUTE_UNIT_MKELVIN }, - [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS }, - [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE } -}; - -static void make_pretty(SkSmartAttributeParsedData *a) { - uint64_t fourtyeight; - - if (!a->name) - return; - - if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN) - return; - - fourtyeight = - ((uint64_t) a->raw[0]) | - (((uint64_t) a->raw[1]) << 8) | - (((uint64_t) a->raw[2]) << 16) | - (((uint64_t) a->raw[3]) << 24) | - (((uint64_t) a->raw[4]) << 32) | - (((uint64_t) a->raw[5]) << 40); - - if (!strcmp(a->name, "spin-up-time")) - a->pretty_value = fourtyeight & 0xFFFF; - else if (!strcmp(a->name, "airflow-temperature-celsius") || - !strcmp(a->name, "temperature-celsius-1") || - !strcmp(a->name, "temperature-celsius-2")) { - a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150; - } else if (!strcmp(a->name, "power-on-minutes")) - a->pretty_value = fourtyeight * 60 * 1000; - else if (!strcmp(a->name, "power-on-seconds")) - a->pretty_value = fourtyeight * 1000; - else if (!strcmp(a->name, "power-on-hours") || - !strcmp(a->name, "loaded-hours") || - !strcmp(a->name, "head-flying-hours")) - a->pretty_value = fourtyeight * 60 * 60 * 1000; - else - a->pretty_value = fourtyeight; -} - -static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) { - const SkIdentifyParsedData *ipd; - - /* These are the simple cases */ - if (attribute_info[id].name) - return &attribute_info[id]; - - /* These are the complex ones */ - if (sk_disk_identify_parse(d, &ipd) < 0) - return NULL; - - switch (id) { - /* We might want to add further special cases/quirks - * here eventually. */ - - case 9: { - - static const SkSmartAttributeInfo maxtor = { - "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS - }; - static const SkSmartAttributeInfo fujitsu = { - "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS - }; - static const SkSmartAttributeInfo others = { - "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS - }; - - if (strstr(ipd->model, "Maxtor") || strstr(ipd->model, "MAXTOR")) - return &maxtor; - else if (strstr(ipd->model, "Fujitsu") || strstr(ipd->model, "FUJITSU")) - return &fujitsu; - - return &others; - } - } - - return NULL; -} - -int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) { - - if (!d->smart_data_valid) { - errno = ENOENT; - return -1; - } - - switch (d->smart_data[362]) { - case 0x00: - case 0x80: - d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER; - break; - - case 0x02: - case 0x82: - d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS; - break; - - case 0x03: - d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS; - break; - - case 0x04: - case 0x84: - d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED; - break; - - case 0x05: - case 0x85: - d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED; - break; - - case 0x06: - case 0x86: - d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL; - break; - - default: - d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN; - break; - } - - d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF); - d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF; - - d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8); - - d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d); - d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d); - d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d); - d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d); - - d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372]; - d->smart_parsed_data.extended_test_polling_minutes = d->smart_data[373] != 0xFF ? d->smart_data[373] : ((uint16_t) d->smart_data[376] << 8 | (uint16_t) d->smart_data[375]); - d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374]; - - *spd = &d->smart_parsed_data; - - return 0; -} - -static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) { - uint8_t *p; - unsigned n; - - if (!d->smart_threshold_data_valid) { - a->threshold_valid = FALSE; - return; - } - - for (n = 0, p = d->smart_threshold_data+2; n < 30; n++, p+=12) - if (p[0] == a->id) - break; - - if (n >= 30) { - a->threshold_valid = FALSE; - return; - } - - a->threshold = p[1]; - a->threshold_valid = TRUE; - - a->good = - a->worst_value > a->threshold && - a->current_value > a->threshold; -} - -int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) { - uint8_t *p; - unsigned n; - - if (!d->smart_data_valid) { - errno = ENOENT; - return -1; - } - - for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) { - SkSmartAttributeParsedData a; - const SkSmartAttributeInfo *i; - char *an = NULL; - - if (p[0] == 0) - continue; - - memset(&a, 0, sizeof(a)); - a.id = p[0]; - a.current_value = p[3]; - a.worst_value = p[4]; - - a.flags = ((uint16_t) p[2] << 8) | p[1]; - a.prefailure = !!(p[1] & 1); - a.online = !!(p[1] & 2); - - memcpy(a.raw, p+5, 6); - - if ((i = lookup_attribute(d, p[0]))) { - a.name = i->name; - a.pretty_unit = i->unit; - } else { - if (asprintf(&an, "attribute-%u", a.id) < 0) { - errno = ENOMEM; - return -1; - } - - a.name = an; - a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN; - } - - make_pretty(&a); - - find_threshold(d, &a); - - cb(d, &a, userdata); - - free(an); - } - - return 0; -} - -static const char *yes_no(SkBool b) { - return b ? "yes" : "no"; -} - -const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) { - - const char * const map[] = { - [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL, - [SK_SMART_ATTRIBUTE_UNIT_NONE] = "", - [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms", - [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors", - [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK" - }; - - if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX) - return NULL; - - return map[unit]; -} - -static char* print_name(char *s, size_t len, uint8_t id, const char *k) { - - if (k) - strncpy(s, k, len); - else - snprintf(s, len, "%u", id); - - s[len-1] = 0; - - return s; - -} - -static char *print_value(char *s, size_t len, const SkSmartAttributeParsedData *a) { - - switch (a->pretty_unit) { - case SK_SMART_ATTRIBUTE_UNIT_MSECONDS: - - if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU) - snprintf(s, len, "%0.1f years", ((double) a->pretty_value)/(1000.0*60*60*24*365)); - else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU) - snprintf(s, len, "%0.1f months", ((double) a->pretty_value)/(1000.0*60*60*24*30)); - else if (a->pretty_value >= 1000LLU*60LLU*60LLU*24LLU) - snprintf(s, len, "%0.1f days", ((double) a->pretty_value)/(1000.0*60*60*24)); - else if (a->pretty_value >= 1000LLU*60LLU*60LLU) - snprintf(s, len, "%0.1f h", ((double) a->pretty_value)/(1000.0*60*60)); - else if (a->pretty_value >= 1000LLU*60LLU) - snprintf(s, len, "%0.1f min", ((double) a->pretty_value)/(1000.0*60)); - else if (a->pretty_value >= 1000LLU) - snprintf(s, len, "%0.1f s", ((double) a->pretty_value)/(1000.0)); - else - snprintf(s, len, "%llu ms", (unsigned long long) a->pretty_value); - - break; - - case SK_SMART_ATTRIBUTE_UNIT_MKELVIN: - snprintf(s, len, "%0.1f C", ((double) a->pretty_value - 273150) / 1000); - break; - - case SK_SMART_ATTRIBUTE_UNIT_SECTORS: - snprintf(s, len, "%llu sectors", (unsigned long long) a->pretty_value); - break; - - case SK_SMART_ATTRIBUTE_UNIT_NONE: - snprintf(s, len, "%llu", (unsigned long long) a->pretty_value); - break; - - case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN: - snprintf(s, len, "n/a"); - break; - - case _SK_SMART_ATTRIBUTE_UNIT_MAX: - assert(FALSE); - } - - s[len-1] = 0; - - return s; -}; - -#define HIGHLIGHT "\x1B[1m" -#define ENDHIGHLIGHT "\x1B[0m" - -static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) { - char name[32]; - char pretty[32]; - char t[32]; - - snprintf(t, sizeof(t), "%3u", a->threshold); - t[sizeof(t)-1] = 0; - - if (!a->good && isatty(1)) - fprintf(stderr, HIGHLIGHT); - - printf("%3u %-27s %3u %3u %-3s %-11s %-7s %-7s %-3s\n", - a->id, - print_name(name, sizeof(name), a->id, a->name), - a->current_value, - a->worst_value, - a->threshold_valid ? t : "n/a", - print_value(pretty, sizeof(pretty), a), - a->prefailure ? "prefail" : "old-age", - a->online ? "online" : "offline", - yes_no(a->good)); - - if (!a->good && isatty(1)) - fprintf(stderr, ENDHIGHLIGHT); -} - -int sk_disk_dump(SkDisk *d) { - int ret; - SkBool awake = FALSE; - - printf("Device: %s\n" - "Size: %lu MiB\n", - d->name, - (unsigned long) (d->size/1024/1024)); - - if (d->identify_data_valid) { - const SkIdentifyParsedData *ipd; - - if ((ret = sk_disk_identify_parse(d, &ipd)) < 0) - return ret; - - printf("Model: [%s]\n" - "Serial: [%s]\n" - "Firmware: [%s]\n" - "SMART Available: %s\n", - ipd->model, - ipd->serial, - ipd->firmware, - yes_no(disk_smart_is_available(d))); - } - - ret = sk_disk_check_sleep_mode(d, &awake); - printf("Awake: %s\n", - ret >= 0 ? yes_no(awake) : "unknown"); - - if (disk_smart_is_available(d)) { - const SkSmartParsedData *spd; - SkBool good; - - if ((ret = sk_disk_smart_status(d, &good)) < 0) - return ret; - - printf("Disk Health Good: %s\n", - yes_no(good)); - - if ((ret = sk_disk_smart_read_data(d)) < 0) - return ret; - - if ((ret = sk_disk_smart_parse(d, &spd)) < 0) - return ret; - - printf("Off-line Data Collection Status: [%s]\n" - "Total Time To Complete Off-Line Data Collection: %u s\n" - "Self-Test Execution Status: [%s]\n" - "Percent Self-Test Remaining: %u%%\n" - "Conveyance Self-Test Available: %s\n" - "Short/Extended Self-Test Available: %s\n" - "Start Self-Test Available: %s\n" - "Abort Self-Test Available: %s\n" - "Short Self-Test Polling Time: %u min\n" - "Extended Self-Test Polling Time: %u min\n" - "Conveyance Self-Test Polling Time: %u min\n", - sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status), - spd->total_offline_data_collection_seconds, - sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status), - spd->self_test_execution_percent_remaining, - yes_no(spd->conveyance_test_available), - yes_no(spd->short_and_extended_test_available), - yes_no(spd->start_test_available), - yes_no(spd->abort_test_available), - spd->short_test_polling_minutes, - spd->extended_test_polling_minutes, - spd->conveyance_test_polling_minutes); - - printf("%3s %-27s %5s %5s %5s %-11s %-7s %-7s %-3s\n", - "ID#", - "Name", - "Value", - "Worst", - "Thres", - "Pretty", - "Type", - "Updates", - "Good"); - - if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0) - return ret; - } - - return 0; -} - -int sk_disk_get_size(SkDisk *d, uint64_t *bytes) { - - *bytes = d->size; - return 0; -} - -int sk_disk_open(const char *name, SkDisk **_d) { - SkDisk *d; - int ret = -1; - struct stat st; - - assert(name); - assert(_d); - - if (!(d = calloc(1, sizeof(SkDisk)))) { - errno = ENOMEM; - goto fail; - } - - if (!(d->name = strdup(name))) { - errno = ENOMEM; - goto fail; - } - - if ((d->fd = open(name, O_RDWR|O_NOCTTY)) < 0) { - ret = d->fd; - goto fail; - } - - if ((ret = fstat(d->fd, &st)) < 0) - goto fail; - - if (!S_ISBLK(st.st_mode)) { - errno = ENODEV; - ret = -1; - goto fail; - } - - /* So, it's a block device. Let's make sure the ioctls work */ - - if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0) - goto fail; - - if (d->size <= 0 || d->size == (uint64_t) -1) { - errno = EIO; - ret = -1; - goto fail; - } - - /* OK, it's a real block device with a size. Find a way to - * identify the device. */ - for (d->type = 0; d->type != SK_DISK_TYPE_UNKNOWN; d->type++) - if (disk_identify_device(d) >= 0) - break; - - /* Check if driver can do SMART, and enable if necessary */ - if (disk_smart_is_available(d)) { - - if (!disk_smart_is_enabled(d)) { - if ((ret = disk_smart_enable(d, TRUE)) < 0) - goto fail; - - if ((ret = disk_identify_device(d)) < 0) - goto fail; - - if (!disk_smart_is_enabled(d)) { - errno = EIO; - ret = -1; - goto fail; - } - } - - disk_smart_read_thresholds(d); - } - - *_d = d; - - return 0; - -fail: - - if (d) - sk_disk_free(d); - - return ret; -} - -void sk_disk_free(SkDisk *d) { - assert(d); - - if (d->fd >= 0) - close(d->fd); - - free(d->name); - free(d); -} diff --git a/smart.h b/smart.h deleted file mode 100644 index bc4100e..0000000 --- a/smart.h +++ /dev/null @@ -1,173 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ - -#ifndef foosmarthfoo -#define foosmarthfoo - -/*** - This file is part of SmartKit. - - Copyright 2008 Lennart Poettering - - SmartKit is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - SmartKit is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with SmartKit. If not, If not, see - . -***/ - -#include - -typedef int SkBool; - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE (!TRUE) -#endif - -/* ATA SMART test type (ATA8 7.52.5.2) */ -typedef enum SkSmartSelfTest { - SK_SMART_SELF_TEST_SHORT = 1, - SK_SMART_SELF_TEST_EXTENDED = 2, - SK_SMART_SELF_TEST_CONVEYANCE = 3, - SK_SMART_SELF_TEST_ABORT = 127 -} SkSmartSelfTest; - -const char* sk_smart_self_test_to_string(SkSmartSelfTest test); - -typedef struct SkIdentifyParsedData { - char serial[21]; - char firmware[9]; - char model[41]; - - /* This structure may be extended at any time without this being - * considered an ABI change. So take care when you copy it. */ -} SkIdentifyParsedData; - -typedef enum SkSmartOfflineDataCollectionStatus { - SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER, - SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS, - SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS, - SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED, - SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED, - SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL, - SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN, - _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX -} SkSmartOfflineDataCollectionStatus; - -const char* sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status); - -typedef enum SkSmartSelfTestExecutionStatus { - SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER = 0, - SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED = 1, - SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED = 2, - SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL = 3, - SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN = 4, - SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL = 5, - SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO = 6, - SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ = 7, - SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING = 8, - SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS = 15, - _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX -} SkSmartSelfTestExecutionStatus; - -const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status); - -typedef struct SkSmartParsedData { - /* Volatile data */ - SkSmartOfflineDataCollectionStatus offline_data_collection_status; - unsigned total_offline_data_collection_seconds; - SkSmartSelfTestExecutionStatus self_test_execution_status; - unsigned self_test_execution_percent_remaining; - - /* Fixed data */ - SkBool short_and_extended_test_available:1; - SkBool conveyance_test_available:1; - SkBool start_test_available:1; - SkBool abort_test_available:1; - - unsigned short_test_polling_minutes; - unsigned extended_test_polling_minutes; - unsigned conveyance_test_polling_minutes; - - /* This structure may be extended at any time without this being - * considered an ABI change. So take care when you copy it. */ -} SkSmartParsedData; - -SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test); -unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test); - -typedef enum SkSmartAttributeUnit { - SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, - SK_SMART_ATTRIBUTE_UNIT_NONE, - SK_SMART_ATTRIBUTE_UNIT_MSECONDS, /* milliseconds */ - SK_SMART_ATTRIBUTE_UNIT_SECTORS, - SK_SMART_ATTRIBUTE_UNIT_MKELVIN, /* millikelvin */ - _SK_SMART_ATTRIBUTE_UNIT_MAX -} SkSmartAttributeUnit; - -const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit); - -typedef struct SkSmartAttributeParsedData { - /* Fixed data */ - uint8_t id; - const char *name; - SkSmartAttributeUnit pretty_unit; /* for pretty_value */ - - uint16_t flags; - - uint8_t threshold; - SkBool threshold_valid:1; - - SkBool online:1; - SkBool prefailure:1; - - /* Volatile data */ - SkBool good:1; - uint8_t current_value, worst_value; - uint64_t pretty_value; - uint8_t raw[6]; - - /* This structure may be extended at any time without this being - * considered an ABI change. So take care when you copy it. */ -} SkSmartAttributeParsedData; - -typedef struct SkDisk SkDisk; - -int sk_disk_open(const char *name, SkDisk **d); - -int sk_disk_get_size(SkDisk *d, uint64_t *bytes); - -int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake); - -int sk_disk_identify_is_available(SkDisk *d, SkBool *available); -int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **data); - -typedef void (*SkSmartAttributeParseCallback)(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata); - -int sk_disk_smart_is_available(SkDisk *d, SkBool *available); -int sk_disk_smart_status(SkDisk *d, SkBool *good); -/* Reading SMART data might cause the disk to wake up from - * sleep. Hence from monitoring daemons make sure to call - * sk_disk_check_power_mode() to check wether the disk is sleeping and - * skip the read if so. */ -int sk_disk_smart_read_data(SkDisk *d); -int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **data); -int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata); -int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test); - -int sk_disk_dump(SkDisk *d); - -void sk_disk_free(SkDisk *d); - -#endif diff --git a/smart.vapi b/smart.vapi deleted file mode 100644 index 3acb02d..0000000 --- a/smart.vapi +++ /dev/null @@ -1,140 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ - -/*** - This file is part of SmartKit. - - Copyright 2008 Lennart Poettering - - SmartKit is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - SmartKit is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with SmartKit. If not, If not, see - . -***/ - -using GLib; - -[CCode (cheader_filename="atasmart.h")] -namespace Smart { - - [CCode (cname="SkSmartSelfTest", cprefix="SK_SMART_SELF_TEST_")] - public enum SmartSelfTest { - SHORT, EXTENDED, CONVEYANCE, ABORT - } - - [Immutable] - [CCode (cname="SkIdentifyParsedData")] - public struct IdentifyParsedData { - public string serial; - public string firmware; - public string model; - } - - [CCode (cname="SkSmartOfflineDataCollectionStatus", cprefix="SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_")] - public enum SmartOfflineDataCollectionStatus { - NEVER, SUCCESS, INPROGRESS, SUSPENDED, ABORTED, FATAL, UNKNOWN - } - - [CCode (cname="sk_smart_offline_data_collection_status_to_string")] - public weak string smart_offline_data_collection_status_to_string(SmartOfflineDataCollectionStatus status); - - - [CCode (cname="SkSmartSelfTestExecutionStatus", cprefix="SK_SMART_SELF_TEST_EXECUTION_STATUS_")] - public enum SmartSelfTestExecutionStatus { - SUCCESS_OR_NEVER, ABORTED, INTERRUPTED, FATAL, ERROR_UNKNOWN, ERROR_ELECTRICAL, ERROR_SERVO, ERROR_READ, ERROR_HANDLING, INPROGRESS - } - - [CCode (cname="sk_smart_self_test_execution_status_to_string")] - public weak string smart_self_test_execution_status_to_string(SmartSelfTestExecutionStatus status); - - [Immutable] - [CCode (cname="SkSmartParsedData")] - public struct SmartParsedData { - public SmartOfflineDataCollectionStatus offline_data_collection_status; - public uint total_offline_data_collection_seconds; - public SmartSelfTestExecutionStatus self_test_execution_status; - public uint self_test_execution_percent_remaining; - - public bool conveyance_test_available; - public bool short_and_extended_test_available; - public bool start_test_available; - public bool abort_test_available; - - public uint short_test_polling_minutes; - public uint extended_test_polling_minutes; - public uint conveyance_test_polling_minutes; - - [CCode (cname="sk_smart_self_test_available")] - public bool self_test_available(SmartSelfTest test); - - [CCode (cname="sk_smart_self_test_polling_minutes")] - public uint self_test_polling_minutes(SmartSelfTest test); - } - - [CCode (cname="SkSmartAttributeUnit", cprefix="SK_SMART_ATTRIBUTE_UNIT_")] - public enum SmartAttributeUnit { - UNKNOWN, NONE, MSECONDS, SECTORS, MKELVIN - } - - [CCode (cname="sk_smart_attribute_unit_to_string")] - public weak string smart_attribute_unit_to_string(SmartAttributeUnit unit); - - [Immutable] - [CCode (cname="SkSmartAttributeParsedData")] - public struct SmartAttributeParsedData { - public uint8 id; - public string name; - public SmartAttributeUnit pretty_unit; - public uint16 flags; - public uint8 threshold; - public bool threshold_valid; - public bool online; - public bool prefailure; - public bool good; - public uint8 current_value; - public uint8 worst_value; - public uint64 pretty_value; - public uint8[6] raw; - } - - [CCode (cname="SkSmartAttributeParseCallback")] - public delegate void SmartAttributeParseCallback(void* disk, SmartAttributeParsedData a); - - [Compact] - [CCode (free_function="sk_disk_free", cname="SkDisk", cprefix="sk_disk_")] - public class Disk { - - public static int open(string name, out Disk disk); - - public int get_size(out uint64 bytes); - - public int check_sleep_mode(out bool awake); - - public int identify_is_available(out bool available); - public int identify_parse(out weak IdentifyParsedData* data); - - public int smart_is_available(out bool available); - public int smart_status(out bool good); - public int smart_read_data(); - public int smart_parse_attributes(SmartAttributeParseCallback cb); - public int smart_parse(out weak SmartParsedData* data); - public int smart_self_test(SmartSelfTest test); - - public int dump(); - } - - /* These two should move to an official vala package */ - [CCode (cname="errno", cheader_filename="errno.h")] - public int errno; - - [CCode (cname="g_strerror", cheader_filename="glib.h")] - public weak string strerror(int err); -} -- cgit