* Home page of code is: http://www.smartmontools.org
*
* Copyright (C) 2002-11 Bruce Allen
- * Copyright (C) 2008-17 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
* Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
#include <ctype.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "int64.h"
#include "atacmdnames.h"
#include "atacmds.h"
#include "ataidentify.h"
#include "dev_interface.h"
#include "ataprint.h"
#include "smartctl.h"
+#include "sg_unaligned.h"
#include "utility.h"
#include "knowndrives.h"
-const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 4573 2017-10-29 15:13:58Z chrfranke $"
+const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
ATAPRINT_H_CVSID;
static const char * get_form_factor(unsigned short word168)
{
+ // Bits 0:3 are the form factor
// Table A.32 of T13/2161-D (ACS-3) Revision 4p, September 19, 2013
// Table 236 of T13/BSR INCITS 529 (ACS-4) Revision 04, August 25, 2014
- switch (word168) {
+ switch (word168 & 0xF) {
case 0x1: return "5.25 inches";
case 0x2: return "3.5 inches";
case 0x3: return "2.5 inches";
case 2: return "SATA II Ext";
case 1: return "SATA 1.0a";
case 0: return "ATA8-AST";
- default: return "Unknown";
+ default: return 0;
}
}
-static const char * get_sata_speed(int level)
+static const char * get_sata_speed(int speed)
{
- if (level <= 0)
+ if (speed <= 0)
return 0;
- switch (level) {
+ switch (speed) {
default: return ">6.0 Gb/s (7)";
case 6: return ">6.0 Gb/s (6)";
case 5: return ">6.0 Gb/s (5)";
}
}
-static const char * get_sata_maxspeed(const ata_identify_device * drive)
+static void jset_sata_speed(const char * key, int value, int speed, const char * str)
{
- unsigned short word076 = drive->words047_079[76-47];
- if (word076 & 0x0001)
- return 0;
- return get_sata_speed(find_msb(word076 & 0x00fe));
+ if (speed <= 0)
+ return;
+ json::ref jref = jglb["interface_speed"][key];
+ jref["sata_value"] = value;
+ if (str)
+ jref["string"] = str;
+ int ups;
+ switch (speed) {
+ case 3: ups = 60; break;
+ case 2: ups = 30; break;
+ case 1: ups = 15; break;
+ default: return;
+ }
+ jref["units_per_second"] = ups;
+ jref["bits_per_unit"] = 100000000;
}
-static const char * get_sata_curspeed(const ata_identify_device * drive)
+static void print_sata_version_and_speed(unsigned short word222,
+ unsigned short word076,
+ unsigned short word077)
{
- unsigned short word077 = drive->words047_079[77-47];
- if (word077 & 0x0001)
- return 0;
- return get_sata_speed((word077 >> 1) & 0x7);
+ int allspeeds = (!(word076 & 0x0001) ? (word076 & 0x00fe) : 0);
+ int maxspeed = (allspeeds ? find_msb(allspeeds) : 0);
+ int curspeed = (!(word077 & 0x0001) ? ((word077 >> 1) & 0x7) : 0);
+
+ const char * verstr = get_sata_version(word222);
+ const char * maxstr = get_sata_speed(maxspeed);
+ const char * curstr = get_sata_speed(curspeed);
+ jout("SATA Version is: %s%s%s%s%s%s\n",
+ (verstr ? verstr : "Unknown"),
+ (maxstr ? ", " : ""), (maxstr ? maxstr : ""),
+ (curstr ? " (current: " : ""), (curstr ? curstr : ""),
+ (curstr ? ")" : ""));
+ if (verstr)
+ jglb["sata_version"]["string"] = verstr;
+ jglb["sata_version"]["value"] = word222 & 0x0fff;
+ jset_sata_speed("max", allspeeds, maxspeed, maxstr);
+ jset_sata_speed("current", curspeed, curspeed, curstr);
}
-
static void print_drive_info(const ata_identify_device * drive,
const ata_size_info & sizes, int rpm,
const drive_settings * dbentry)
ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
// Print model family if known
- if (dbentry && *dbentry->modelfamily)
- pout("Model Family: %s\n", dbentry->modelfamily);
+ if (dbentry && *dbentry->modelfamily) {
+ jout("Model Family: %s\n", dbentry->modelfamily);
+ jglb["model_family"] = dbentry->modelfamily;
+ }
- pout("Device Model: %s\n", infofound(model));
+ jout("Device Model: %s\n", infofound(model));
+ jglb["model_name"] = model;
if (!dont_print_serial_number) {
- pout("Serial Number: %s\n", infofound(serial));
+ jout("Serial Number: %s\n", infofound(serial));
+ jglb["serial_number"] = serial;
unsigned oui = 0; uint64_t unique_id = 0;
int naa = ata_get_wwn(drive, oui, unique_id);
- if (naa >= 0)
- pout("LU WWN Device Id: %x %06x %09" PRIx64 "\n", naa, oui, unique_id);
+ if (naa >= 0) {
+ jout("LU WWN Device Id: %x %06x %09" PRIx64 "\n", naa, oui, unique_id);
+ jglb["wwn"]["naa"] = naa;
+ jglb["wwn"]["oui"] = oui;
+ jglb["wwn"]["id"] = unique_id;
+ }
}
// Additional Product Identifier (OEM Id) string in words 170-173
if (0x2020 <= drive->words088_255[170-88] && drive->words088_255[170-88] <= 0x7e7e) {
char add[8+1];
ata_format_id_string(add, (const unsigned char *)(drive->words088_255+170-88), sizeof(add)-1);
- if (add[0])
- pout("Add. Product Id: %s\n", add);
+ if (add[0]) {
+ jout("Add. Product Id: %s\n", add);
+ jglb["ata_additional_product_id"] = add;
+ }
}
- pout("Firmware Version: %s\n", infofound(firmware));
+ jout("Firmware Version: %s\n", infofound(firmware));
+ jglb["firmware_version"] = firmware;
if (sizes.capacity) {
// Print capacity
char num[64], cap[32];
- pout("User Capacity: %s bytes [%s]\n",
+ jout("User Capacity: %s bytes [%s]\n",
format_with_thousands_sep(num, sizeof(num), sizes.capacity),
format_capacity(cap, sizeof(cap), sizes.capacity));
+ jglb["user_capacity"]["blocks"].set_unsafe_uint64(sizes.sectors);
+ jglb["user_capacity"]["bytes"].set_unsafe_uint64(sizes.capacity);
// Print sector sizes.
if (sizes.phy_sector_size == sizes.log_sector_size)
- pout("Sector Size: %u bytes logical/physical\n", sizes.log_sector_size);
+ jout("Sector Size: %u bytes logical/physical\n", sizes.log_sector_size);
else {
- pout("Sector Sizes: %u bytes logical, %u bytes physical",
- sizes.log_sector_size, sizes.phy_sector_size);
+ jout("Sector Sizes: %u bytes logical, %u bytes physical",
+ sizes.log_sector_size, sizes.phy_sector_size);
if (sizes.log_sector_offset)
pout(" (offset %u bytes)", sizes.log_sector_offset);
- pout("\n");
+ jout("\n");
}
+ jglb["logical_block_size"] = sizes.log_sector_size;
+ jglb["physical_block_size"] = sizes.phy_sector_size;
}
// Print nominal media rotation rate if reported
if (rpm) {
if (rpm == 1)
- pout("Rotation Rate: Solid State Device\n");
+ jout("Rotation Rate: Solid State Device\n");
else if (rpm > 1)
- pout("Rotation Rate: %d rpm\n", rpm);
+ jout("Rotation Rate: %d rpm\n", rpm);
else
pout("Rotation Rate: Unknown (0x%04x)\n", -rpm);
+ if (rpm > 0)
+ jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm);
}
// Print form factor if reported
if (word168) {
const char * form_factor = get_form_factor(word168);
if (form_factor)
- pout("Form Factor: %s\n", form_factor);
+ jout("Form Factor: %s\n", form_factor);
else
- pout("Form Factor: Unknown (0x%04x)\n", word168);
+ jout("Form Factor: Unknown (0x%04x)\n", word168);
+ jglb["form_factor"]["ata_value"] = word168;
+ jglb["form_factor"]["name"] = form_factor;
}
// See if drive is recognized
- pout("Device is: %s\n", !dbentry ?
+ jout("Device is: %s\n", !dbentry ?
"Not in smartctl database [for details use: -P showall]":
"In smartctl database [for details use: -P show]");
+ jglb["in_smartctl_database"] = !!dbentry;
// Print ATA version
std::string ataver;
ataver += " (minor revision not indicated)";
}
}
- pout("ATA Version is: %s\n", infofound(ataver.c_str()));
+ jout("ATA Version is: %s\n", infofound(ataver.c_str()));
+ if (!ataver.empty()) {
+ jglb["ata_version"]["string"] = ataver;
+ jglb["ata_version"]["major_value"] = drive->major_rev_num;
+ jglb["ata_version"]["minor_value"] = drive->minor_rev_num;
+ }
// Print Transport specific version
unsigned short word222 = drive->words088_255[222-88];
}
break;
case 0x1: // SATA
- {
- const char * sataver = get_sata_version(word222);
- const char * maxspeed = get_sata_maxspeed(drive);
- const char * curspeed = get_sata_curspeed(drive);
- pout("SATA Version is: %s%s%s%s%s%s\n", sataver,
- (maxspeed ? ", " : ""), (maxspeed ? maxspeed : ""),
- (curspeed ? " (current: " : ""), (curspeed ? curspeed : ""),
- (curspeed ? ")" : ""));
- }
+ print_sata_version_and_speed(word222,
+ drive->words047_079[76-47],
+ drive->words047_079[77-47]);
break;
case 0xe: // PCIe (ACS-4)
pout("Transport Type: PCIe (0x%03x)\n", word222 & 0x0fff);
}
// print current time and date and timezone
- char timedatetz[DATEANDEPOCHLEN]; dateandtimezone(timedatetz);
- pout("Local Time is: %s\n", timedatetz);
+ time_t now = time(0);
+ char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now);
+ jout("Local Time is: %s\n", timedatetz);
+ jglb["local_time"]["time_t"] = now;
+ jglb["local_time"]["asctime"] = timedatetz;
// Print warning message, if there is one
if (dbentry && *dbentry->warningmsg)
// prints verbose value Off-line data collection status byte
static void PrintSmartOfflineStatus(const ata_smart_values * data)
{
- pout("Offline data collection status: (0x%02x)\t",
+ json::ref jref = jglb["ata_smart_data"]["offline_data_collection"]["status"];
+
+ jout("Offline data collection status: (0x%02x)\t",
(int)data->offline_data_collection_status);
+ jref["value"] = data->offline_data_collection_status;
// Off-line data collection status byte is not a reserved
// or vendor specific value
- pout("Offline data collection activity\n"
+ jout("Offline data collection activity\n"
"\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status));
-
+ jref["string"] = OfflineDataCollectionStatus(data->offline_data_collection_status);
+ switch (data->offline_data_collection_status & 0x7f) {
+ case 0x02: jref["passed"] = true; break;
+ case 0x06: jref["passed"] = false; break;
+ }
+
// Report on Automatic Data Collection Status. Only IBM documents
// this bit. See SFF 8035i Revision 2 for details.
if (data->offline_data_collection_status & 0x80)
static void PrintSmartSelfExecStatus(const ata_smart_values * data,
firmwarebug_defs firmwarebugs)
{
- pout("Self-test execution status: ");
-
- switch (data->self_test_exec_status >> 4)
- {
- case 0:
- pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
- break;
- case 1:
- pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("the host.\n");
- break;
- case 2:
- pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("by the host with a hard or soft reset.\n");
- break;
- case 3:
- pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("occurred while the device was executing\n\t\t\t\t\t");
- pout("its self-test routine and the device \n\t\t\t\t\t");
- pout("was unable to complete the self-test \n\t\t\t\t\t");
- pout("routine.\n");
- break;
- case 4:
- pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("a test element that failed and the test\n\t\t\t\t\t");
- pout("element that failed is not known.\n");
- break;
- case 5:
- pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("the electrical element of the test\n\t\t\t\t\t");
- pout("failed.\n");
- break;
- case 6:
- pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("the servo (and/or seek) element of the \n\t\t\t\t\t");
- pout("test failed.\n");
- break;
- case 7:
- pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("the read element of the test failed.\n");
- break;
- case 8:
- pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("a test element that failed and the\n\t\t\t\t\t");
- pout("device is suspected of having handling\n\t\t\t\t\t");
- pout("damage.\n");
- break;
- case 15:
- if (firmwarebugs.is_set(BUG_SAMSUNG3) && data->self_test_exec_status == 0xf0) {
- pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("with unknown result or self-test in\n\t\t\t\t\t");
- pout("progress with less than 10%% remaining.\n");
- }
- else {
- pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t",
- (int)data->self_test_exec_status);
- pout("%1d0%% of test remaining.\n",
- (int)(data->self_test_exec_status & 0x0f));
- }
- break;
- default:
- pout("(%4d)\tReserved.\n",
- (int)data->self_test_exec_status);
- break;
- }
-
+ unsigned char status = data->self_test_exec_status;
+ jout("Self-test execution status: ");
+
+ switch (data->self_test_exec_status >> 4) {
+ case 0:
+ jout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", status);
+ jout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
+ break;
+ case 1:
+ jout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t", status);
+ jout("the host.\n");
+ break;
+ case 2:
+ jout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t", status);
+ jout("by the host with a hard or soft reset.\n");
+ break;
+ case 3:
+ jout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t", status);
+ jout("occurred while the device was executing\n\t\t\t\t\t");
+ jout("its self-test routine and the device \n\t\t\t\t\t");
+ jout("was unable to complete the self-test \n\t\t\t\t\t");
+ jout("routine.\n");
+ break;
+ case 4:
+ jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+ jout("a test element that failed and the test\n\t\t\t\t\t");
+ jout("element that failed is not known.\n");
+ break;
+ case 5:
+ jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+ jout("the electrical element of the test\n\t\t\t\t\t");
+ jout("failed.\n");
+ break;
+ case 6:
+ jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+ jout("the servo (and/or seek) element of the \n\t\t\t\t\t");
+ jout("test failed.\n");
+ break;
+ case 7:
+ jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+ jout("the read element of the test failed.\n");
+ break;
+ case 8:
+ jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+ jout("a test element that failed and the\n\t\t\t\t\t");
+ jout("device is suspected of having handling\n\t\t\t\t\t");
+ jout("damage.\n");
+ break;
+ case 15:
+ if (firmwarebugs.is_set(BUG_SAMSUNG3) && data->self_test_exec_status == 0xf0) {
+ pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", status);
+ pout("with unknown result or self-test in\n\t\t\t\t\t");
+ pout("progress with less than 10%% remaining.\n");
+ }
+ else {
+ jout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t", status);
+ jout("%1d0%% of test remaining.\n", status & 0x0f);
+ }
+ break;
+ default:
+ jout("(%4d)\tReserved.\n", status);
+ break;
+ }
+
+ json::ref jref = jglb["ata_smart_data"]["self_test"]["status"];
+
+ jref["value"] = status;
+ const char * msg;
+ // TODO: Use common function for smartctl/smartd
+ switch (status >> 4) {
+ case 0x0: msg = "completed without error"; break;
+ case 0x1: msg = "was aborted by the host"; break;
+ case 0x2: msg = "was interrupted by the host with a reset"; break;
+ case 0x3: msg = "could not complete due to a fatal or unknown error"; break;
+ case 0x4: msg = "completed with error (unknown test element)"; break;
+ case 0x5: msg = "completed with error (electrical test element)"; break;
+ case 0x6: msg = "completed with error (servo/seek test element)"; break;
+ case 0x7: msg = "completed with error (read test element)"; break;
+ case 0x8: msg = "completed with error (handling damage?)"; break;
+ default: msg = 0;
+ }
+ if (msg) {
+ jref["string"] = msg;
+ switch (status >> 4) {
+ case 0x1: case 0x2: case 0x3: break; // aborted -> unknown
+ default: jref["passed"] = ((status >> 4) == 0x0);
+ }
+ }
+ else if ((status >> 4) == 0xf) {
+ jref["string"] = strprintf("in progress, %u0%% remaining", status & 0xf);
+ jref["remaining_percent"] = (status & 0xf) * 10;
+ }
}
static void PrintSmartTotalTimeCompleteOffline (const ata_smart_values * data)
{
- pout("Total time to complete Offline \n");
- pout("data collection: \t\t(%5d) seconds.\n",
+ jout("Total time to complete Offline \n");
+ jout("data collection: \t\t(%5d) seconds.\n",
(int)data->total_time_to_complete_off_line);
+
+ jglb["ata_smart_data"]["offline_data_collection"]["completion_seconds"] =
+ data->total_time_to_complete_off_line;
}
static void PrintSmartOfflineCollectCap(const ata_smart_values *data)
{
- pout("Offline data collection\n");
- pout("capabilities: \t\t\t (0x%02x) ",
+ json::ref jref = jglb["ata_smart_data"]["capabilities"];
+
+ jout("Offline data collection\n");
+ jout("capabilities: \t\t\t (0x%02x) ",
(int)data->offline_data_collection_capability);
+ jref["values"][0] = data->offline_data_collection_capability;
if (data->offline_data_collection_capability == 0x00){
- pout("\tOffline data collection not supported.\n");
+ jout("\tOffline data collection not supported.\n");
}
else {
- pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
+ jout( "%s\n", isSupportExecuteOfflineImmediate(data)?
"SMART execute Offline immediate." :
"No SMART execute Offline immediate.");
-
+ jref["exec_offline_immediate_supported"] = isSupportExecuteOfflineImmediate(data);
+
+ // TODO: Bit 1 is vendor specific
pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)?
"Auto Offline data collection on/off support.":
"No Auto Offline data collection support.");
-
- pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)?
+
+ jout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)?
"Abort Offline collection upon new\n\t\t\t\t\tcommand.":
"Suspend Offline collection upon new\n\t\t\t\t\tcommand.");
-
- pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)?
+ jref["offline_is_aborted_upon_new_cmd"] = isSupportOfflineAbort(data);
+
+ jout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)?
"Offline surface scan supported.":
"No Offline surface scan supported.");
-
- pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)?
+ jref["offline_surface_scan_supported"] = isSupportOfflineSurfaceScan(data);
+
+ jout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)?
"Self-test supported.":
"No Self-test supported.");
+ jref["self_tests_supported"] = isSupportSelfTest(data);
- pout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)?
+ jout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)?
"Conveyance Self-test supported.":
"No Conveyance Self-test supported.");
+ jref["conveyance_self_test_supported"] = isSupportConveyanceSelfTest(data);
- pout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)?
+ jout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)?
"Selective Self-test supported.":
"No Selective Self-test supported.");
+ jref["selective_self_test_supported"] = isSupportSelectiveSelfTest(data);
}
}
static void PrintSmartCapability(const ata_smart_values *data)
{
- pout("SMART capabilities: ");
- pout("(0x%04x)\t", (int)data->smart_capability);
-
- if (data->smart_capability == 0x00)
- {
- pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
- }
- else
- {
-
- pout( "%s\n", (data->smart_capability & 0x01)?
- "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.":
- "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode.");
-
- if ( data->smart_capability & 0x02 )
- {
- pout("\t\t\t\t\tSupports SMART auto save timer.\n");
- }
- }
+ json::ref jref = jglb["ata_smart_data"]["capabilities"];
+
+ jout("SMART capabilities: ");
+ jout("(0x%04x)\t", (int)data->smart_capability);
+ jref["values"][1] = data->smart_capability;
+
+ if (data->smart_capability == 0x00)
+ jout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
+ else {
+ jout("%s\n", (data->smart_capability & 0x01)?
+ "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.":
+ "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode.");
+ jref["attribute_autosave_enabled"] = !!(data->smart_capability & 0x01);
+
+ // TODO: Info possibly invalid or misleading
+ // ATA-3 - ATA-5: Bit shall be set
+ // ATA-6 - ACS-3: Bit shall be set to indicate support for
+ // SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE
+ if (data->smart_capability & 0x02)
+ pout("\t\t\t\t\tSupports SMART auto save timer.\n");
+ }
}
static void PrintSmartErrorLogCapability(const ata_smart_values * data, const ata_identify_device * identity)
{
- pout("Error logging capability: ");
-
- if ( isSmartErrorLogCapable(data, identity) )
- {
- pout(" (0x%02x)\tError logging supported.\n",
- (int)data->errorlog_capability);
- }
- else {
- pout(" (0x%02x)\tError logging NOT supported.\n",
- (int)data->errorlog_capability);
- }
+ bool capable = isSmartErrorLogCapable(data, identity);
+ jout("Error logging capability: (0x%02x)\tError logging %ssupported.\n",
+ data->errorlog_capability, (capable ? "" : "NOT "));
+ jglb["ata_smart_data"]["capabilities"]["error_logging_supported"] = capable;
}
static void PrintSmartShortSelfTestPollingTime(const ata_smart_values * data)
{
- pout("Short self-test routine \n");
- if (isSupportSelfTest(data))
- pout("recommended polling time: \t (%4d) minutes.\n",
+ jout("Short self-test routine \n");
+ if (isSupportSelfTest(data)) {
+ jout("recommended polling time: \t (%4d) minutes.\n",
(int)data->short_test_completion_time);
+ jglb["ata_smart_data"]["self_test"]["polling_minutes"]["short"] =
+ data->short_test_completion_time;
+ }
else
- pout("recommended polling time: \t Not Supported.\n");
+ jout("recommended polling time: \t Not Supported.\n");
}
static void PrintSmartExtendedSelfTestPollingTime(const ata_smart_values * data)
{
- pout("Extended self-test routine\n");
- if (isSupportSelfTest(data))
- pout("recommended polling time: \t (%4d) minutes.\n",
+ jout("Extended self-test routine\n");
+ if (isSupportSelfTest(data)) {
+ jout("recommended polling time: \t (%4d) minutes.\n",
TestTime(data, EXTEND_SELF_TEST));
+ jglb["ata_smart_data"]["self_test"]["polling_minutes"]["extended"] =
+ TestTime(data, EXTEND_SELF_TEST);
+ }
else
- pout("recommended polling time: \t Not Supported.\n");
+ jout("recommended polling time: \t Not Supported.\n");
}
static void PrintSmartConveyanceSelfTestPollingTime(const ata_smart_values * data)
{
- pout("Conveyance self-test routine\n");
- if (isSupportConveyanceSelfTest(data))
- pout("recommended polling time: \t (%4d) minutes.\n",
+ jout("Conveyance self-test routine\n");
+ if (isSupportConveyanceSelfTest(data)) {
+ jout("recommended polling time: \t (%4d) minutes.\n",
(int)data->conveyance_test_completion_time);
+ jglb["ata_smart_data"]["self_test"]["polling_minutes"]["conveyance"] =
+ data->conveyance_test_completion_time;
+ }
else
- pout("recommended polling time: \t Not Supported.\n");
+ jout("recommended polling time: \t Not Supported.\n");
}
// Check SMART attribute table for Threshold failure
return 0;
}
+static void set_json_globals_from_smart_attrib(int id, const char * name,
+ const ata_vendor_attr_defs & defs,
+ uint64_t rawval)
+{
+ switch (id) {
+ case 9:
+ if (!str_starts_with(name, "Power_On_"))
+ return;
+ {
+ int minutes = -1;
+ switch (defs[id].raw_format) {
+ case RAWFMT_DEFAULT: case RAWFMT_RAW48: case RAWFMT_RAW64:
+ case RAWFMT_RAW16_OPT_RAW16: case RAWFMT_RAW24_OPT_RAW8: break;
+ case RAWFMT_SEC2HOUR: minutes = (rawval / 60) % 60; rawval /= 60*60; break;
+ case RAWFMT_MIN2HOUR: minutes = rawval % 60; rawval /= 60; break;
+ case RAWFMT_HALFMIN2HOUR: minutes = (rawval / 2) % 60; rawval /= 2*60; break;
+ case RAWFMT_MSEC24_HOUR32:
+ minutes = (int)(rawval >> 32) / (1000*60);
+ if (minutes >= 60)
+ minutes = -1;
+ rawval &= 0xffffffffULL;
+ break;
+ default: return;
+ }
+ if (rawval > 0x00ffffffULL)
+ return; // assume bogus value
+ jglb["power_on_time"]["hours"] = rawval;
+ if (minutes >= 0)
+ jglb["power_on_time"]["minutes"] = minutes;
+ }
+ break;
+ case 12:
+ if (strcmp(name, "Power_Cycle_Count"))
+ return;
+ switch (defs[id].raw_format) {
+ case RAWFMT_DEFAULT: case RAWFMT_RAW48: case RAWFMT_RAW64:
+ case RAWFMT_RAW16_OPT_RAW16: case RAWFMT_RAW24_OPT_RAW8: break;
+ default: return;
+ }
+ if (rawval > 0x00ffffffULL)
+ return; // assume bogus value
+ jglb["power_cycle_count"] = rawval;
+ break;
+ //case 194:
+ // Temperature set separately from ata_return_temperature_value() below
+ }
+}
+
// onlyfailed=0 : print all attribute values
// onlyfailed=1: just ones that are currently failed and have prefailure bit set
// onlyfailed=2: ones that are failed, or have failed with or without prefailure bit set
bool needheader = true;
// step through all vendor attributes
- for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+ for (int i = 0, ji = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
const ata_smart_attribute & attr = data->vendor_attributes[i];
// Check attribute and threshold
// print header only if needed
if (needheader) {
if (!onlyfailed) {
- pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber);
- pout("Vendor Specific SMART Attributes with Thresholds:\n");
+ jout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber);
+ jglb["ata_smart_attributes"]["revision"] = data->revnumber;
+ jout("Vendor Specific SMART Attributes with Thresholds:\n");
}
if (!brief)
- pout("ID#%s ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE\n",
+ jout("ID#%s ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE\n",
(!hexid ? "" : " "));
else
- pout("ID#%s ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE\n",
+ jout("ID#%s ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE\n",
(!hexid ? "" : " "));
needheader = false;
}
std::string attrname = ata_get_smart_attr_name(attr.id, defs, rpm);
std::string rawstr = ata_format_attr_raw_value(attr, defs);
+ char flagstr[] = {
+ (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) ? 'P' : '-'),
+ (ATTRIBUTE_FLAGS_ONLINE(attr.flags) ? 'O' : '-'),
+ (ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags) ? 'S' : '-'),
+ (ATTRIBUTE_FLAGS_ERRORRATE(attr.flags) ? 'R' : '-'),
+ (ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags) ? 'C' : '-'),
+ (ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags) ? 'K' : '-'),
+ (ATTRIBUTE_FLAGS_OTHER(attr.flags) ? '+' : ' '),
+ 0
+ };
+
if (!brief)
- pout("%s %-24s0x%04x %-4s %-4s %-4s %-10s%-9s%-12s%s\n",
+ jout("%s %-24s0x%04x %-4s %-4s %-4s %-10s%-9s%-12s%s\n",
idstr.c_str(), attrname.c_str(), attr.flags,
valstr.c_str(), worstr.c_str(), threstr.c_str(),
(ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) ? "Pre-fail" : "Old_age"),
: " -" ) ,
rawstr.c_str());
else
- pout("%s %-24s%c%c%c%c%c%c%c %-4s %-4s %-4s %-5s%s\n",
- idstr.c_str(), attrname.c_str(),
- (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) ? 'P' : '-'),
- (ATTRIBUTE_FLAGS_ONLINE(attr.flags) ? 'O' : '-'),
- (ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags) ? 'S' : '-'),
- (ATTRIBUTE_FLAGS_ERRORRATE(attr.flags) ? 'R' : '-'),
- (ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags) ? 'C' : '-'),
- (ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags) ? 'K' : '-'),
- (ATTRIBUTE_FLAGS_OTHER(attr.flags) ? '+' : ' '),
+ jout("%s %-24s%s %-4s %-4s %-4s %-5s%s\n",
+ idstr.c_str(), attrname.c_str(), flagstr,
valstr.c_str(), worstr.c_str(), threstr.c_str(),
(state == ATTRSTATE_FAILED_NOW ? "NOW" :
state == ATTRSTATE_FAILED_PAST ? "Past"
: "-" ),
rawstr.c_str());
+ if (!jglb.is_enabled())
+ continue;
+
+ json::ref jref = jglb["ata_smart_attributes"]["table"][ji++];
+ jref["id"] = attr.id;
+ jref["name"] = attrname;
+ if (state > ATTRSTATE_NO_NORMVAL)
+ jref["value"] = attr.current;
+ if (!(defs[attr.id].flags & ATTRFLAG_NO_WORSTVAL))
+ jref["worst"] = attr.worst;
+ if (state > ATTRSTATE_NO_THRESHOLD) {
+ jref["thresh"] = threshold;
+ jref["when_failed"] = (state == ATTRSTATE_FAILED_NOW ? "now" :
+ state == ATTRSTATE_FAILED_PAST ? "past"
+ : "" );
+ }
+
+ json::ref jreff = jref["flags"];
+ jreff["value"] = attr.flags;
+ jreff["string"] = flagstr;
+ jreff["prefailure"] = !!ATTRIBUTE_FLAGS_PREFAILURE(attr.flags);
+ jreff["updated_online"] = !!ATTRIBUTE_FLAGS_ONLINE(attr.flags);
+ jreff["performance"] = !!ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags);
+ jreff["error_rate"] = !!ATTRIBUTE_FLAGS_ERRORRATE(attr.flags);
+ jreff["event_count"] = !!ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags);
+ jreff["auto_keep"] = !!ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags);
+ if (ATTRIBUTE_FLAGS_OTHER(attr.flags))
+ jreff["other"] = ATTRIBUTE_FLAGS_OTHER(attr.flags);
+
+ uint64_t rawval = ata_get_attr_raw_value(attr, defs);
+ jref["raw"]["value"] = rawval;
+ jref["raw"]["string"] = rawstr;
+
+ set_json_globals_from_smart_attrib(attr.id, attrname.c_str(), defs, rawval);
}
if (!needheader) {
if (!onlyfailed && brief) {
int n = (!hexid ? 28 : 29);
- pout("%*s||||||_ K auto-keep\n"
+ jout("%*s||||||_ K auto-keep\n"
"%*s|||||__ C event count\n"
"%*s||||___ R error rate\n"
"%*s|||____ S speed/performance\n"
}
pout("\n");
}
+
+ if (!jglb.is_enabled())
+ return;
+
+ // Protocol independent temperature
+ unsigned char t = ata_return_temperature_value(data, defs);
+ if (t)
+ jglb["temperature"]["current"] = t;
}
// Print SMART related SCT capabilities
unsigned short sctcaps = drive->words088_255[206-88];
if (!(sctcaps & 0x01))
return;
- pout("SCT capabilities: \t (0x%04x)\tSCT Status supported.\n", sctcaps);
+ json::ref jref = jglb["ata_sct_capabilities"];
+ jout("SCT capabilities: \t (0x%04x)\tSCT Status supported.\n", sctcaps);
+ jref["value"] = sctcaps;
if (sctcaps & 0x08)
- pout("\t\t\t\t\tSCT Error Recovery Control supported.\n");
+ jout("\t\t\t\t\tSCT Error Recovery Control supported.\n");
+ jref["error_recovery_control_supported"] = !!(sctcaps & 0x08);
if (sctcaps & 0x10)
- pout("\t\t\t\t\tSCT Feature Control supported.\n");
+ jout("\t\t\t\t\tSCT Feature Control supported.\n");
+ jref["feature_control_supported"] = !!(sctcaps & 0x10);
if (sctcaps & 0x20)
- pout("\t\t\t\t\tSCT Data Table supported.\n");
+ jout("\t\t\t\t\tSCT Data Table supported.\n");
+ jref["data_table_supported"] = !!(sctcaps & 0x20);
}
static void PrintGeneralSmartValues(const ata_smart_values *data, const ata_identify_device *drive,
firmwarebug_defs firmwarebugs)
{
- pout("General SMART Values:\n");
+ jout("General SMART Values:\n");
PrintSmartOfflineStatus(data);
PrintSmartErrorLogCapability(data, drive);
- pout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)?
+ jout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)?
"General Purpose Logging supported.":
"No General Purpose Logging support.");
+ jglb["ata_smart_data"]["capabilities"]["gp_logging_supported"] =
+ isGeneralPurposeLoggingCapable(drive);
if (isSupportSelfTest(data)){
PrintSmartShortSelfTestPollingTime (data);
ataPrintSCTCapability(drive);
- pout("\n");
+ jout("\n");
}
// Get # sectors of a log addr, 0 if log does not exist.
static void PrintLogDirectories(const ata_smart_log_directory * gplogdir,
const ata_smart_log_directory * smartlogdir)
{
- if (gplogdir)
- pout("General Purpose Log Directory Version %u\n", gplogdir->logversion);
- if (smartlogdir)
- pout("SMART %sLog Directory Version %u%s\n",
+ json::ref jref = jglb["ata_log_directory"];
+ if (gplogdir) {
+ jout("General Purpose Log Directory Version %u\n", gplogdir->logversion);
+ jref["gp_dir_version"] = gplogdir->logversion;
+ }
+ if (smartlogdir) {
+ jout("SMART %sLog Directory Version %u%s\n",
(gplogdir ? " " : ""), smartlogdir->logversion,
(smartlogdir->logversion==1 ? " [multi-sector log support]" : ""));
+ jref["smart_dir_version"] = smartlogdir->logversion;
+ jref["smart_dir_multi_sector"] = (smartlogdir->logversion == 1);
+ }
- pout("Address Access R/W Size Description\n");
+ jout("Address Access R/W Size Description\n");
- for (unsigned i = 0; i <= 0xff; i++) {
+ for (unsigned i = 0, ji = 0; i <= 0xff; i++) {
// Get number of sectors
unsigned smart_numsect = GetNumLogSectors(smartlogdir, i, false);
unsigned gp_numsect = GetNumLogSectors(gplogdir , i, true );
const char * name = GetLogName(i);
const char * rw = get_log_rw(i);
- if (i2 > i) {
- pout("0x%02x-0x%02x %-6s %-3s %5u %s\n", i, i2, acc, rw, size, name);
- i = i2;
- }
+ if (i2 > i)
+ jout("0x%02x-0x%02x %-6s %-3s %5u %s\n", i, i2, acc, rw, size, name);
else if (acc)
- pout( "0x%02x %-6s %-3s %5u %s\n", i, acc, rw, size, name);
+ jout( "0x%02x %-6s %-3s %5u %s\n", i, acc, rw, size, name);
else {
// GPL and SL support different sizes
- pout( "0x%02x %-6s %-3s %5u %s\n", i, "GPL", rw, gp_numsect, name);
- pout( "0x%02x %-6s %-3s %5u %s\n", i, "SL", rw, smart_numsect, name);
+ jout( "0x%02x %-6s %-3s %5u %s\n", i, "GPL", rw, gp_numsect, name);
+ jout( "0x%02x %-6s %-3s %5u %s\n", i, "SL", rw, smart_numsect, name);
+ }
+
+ for (;;) {
+ json::ref jrefi = jref["table"][ji++];
+ jrefi["address"] = i;
+ jrefi["name"] = name;
+ if (rw[0] == 'R' && rw[1] && rw[2]) {
+ jrefi["read"] = true;
+ jrefi["write"] = (rw[2] == 'W');
+ }
+ if (gp_numsect)
+ jrefi["gp_sectors"] = gp_numsect;
+ if (smart_numsect)
+ jrefi["smart_sectors"] = smart_numsect;
+ if (i >= i2)
+ break;
+ i++;
}
}
- pout("\n");
+ jout("\n");
}
// Print hexdump of log pages.
return "Unknown Statistics";
}
-static void print_device_statistics_page(const unsigned char * data, int page)
+static void set_json_globals_from_device_statistics(int page, int offset, int64_t val)
+{
+ switch (page) {
+ case 1:
+ switch (offset) {
+ case 0x008: jglb["power_cycle_count"] = val; break; // ~= Lifetime Power-On Resets
+ case 0x010: jglb["power_on_time"]["hours"]= val; break;
+ }
+ break;
+ case 5:
+ switch (offset) {
+ case 0x008: jglb["temperature"]["current"] = val; break;
+ case 0x020: jglb["temperature"]["lifetime_max"] = val; break;
+ case 0x028: jglb["temperature"]["lifetime_min"] = val; break;
+ case 0x050: jglb["temperature"]["lifetime_over_limit_minutes"] = val; break;
+ case 0x058: jglb["temperature"]["op_limit_max"] = val; break;
+ case 0x060: jglb["temperature"]["lifetime_under_limit_minutes"] = val; break;
+ case 0x068: jglb["temperature"]["op_limit_min"] = val; break;
+ }
+ break;
+ }
+}
+
+static void print_device_statistics_page(const json::ref & jref, const unsigned char * data, int page)
{
const devstat_entry_info * info = (page < num_devstat_infos ? devstat_infos[page] : 0);
const char * name = get_device_statistics_page_name(page);
return;
}
- pout("0x%02x%s%s (rev %d) ==\n", page, line, name, data[0] | (data[1] << 8));
+ int rev = data[0] | (data[1] << 8);
+ jout("0x%02x%s%s (rev %d) ==\n", page, line, name, rev);
+ jref["number"] = page;
+ jref["name"] = name;
+ jref["revision"] = rev;
// Print entries
+ int ji = 0;
for (int i = 1, offset = 8; offset < 512-7; i++, offset+=8) {
// Check for last known entry
if (info && !info[i].size)
break;
}
+ // Get value name
+ const char * valname = (info ? info[i].name :
+ (page == 0xff) ? "Vendor Specific" // ACS-4
+ : "Unknown" );
+
// Get value size, default to max if unknown
int size = (info ? info[i].size : 7);
+ // Get flags (supported flag already checked above)
+ bool valid = !!(flags & 0x40);
+ bool normalized = !!(flags & 0x20);
+ bool supports_dsn = !!(flags & 0x10); // ACS-3
+ bool monitored_condition_met = !!(flags & 0x08); // ACS-3
+ unsigned char reserved_flags = (flags & 0x07);
+
// Format value
+ int64_t val = 0;
char valstr[32];
- if (flags & 0x40) { // valid flag
+ if (valid) {
// Get value
- int64_t val;
if (size < 0) {
val = (signed char)data[offset];
}
else {
- val = 0;
for (int j = 0; j < size; j++)
val |= (int64_t)data[offset+j] << (j*8);
}
valstr[0] = '-'; valstr[1] = 0;
}
- pout("0x%02x 0x%03x %d %15s %c%c%c%c %s\n",
- page, offset,
- abs(size),
- valstr,
- ((flags & 0x20) ? 'N' : '-'), // normalized statistics
- ((flags & 0x10) ? 'D' : '-'), // supports DSN (ACS-3)
- ((flags & 0x08) ? 'C' : '-'), // monitored condition met (ACS-3)
- ((flags & 0x07) ? '+' : ' '), // reserved flags
- ( info ? info[i].name :
- (page == 0xff) ? "Vendor Specific" // ACS-4
- : "Unknown" ));
+ char flagstr[] = {
+ (valid ? 'V' : '-'), // JSON only
+ (normalized ? 'N' : '-'),
+ (supports_dsn ? 'D' : '-'),
+ (monitored_condition_met ? 'C' : '-'),
+ (reserved_flags ? '+' : ' '),
+ 0
+ };
+
+ jout("0x%02x 0x%03x %d %15s %s %s\n",
+ page, offset, abs(size), valstr, flagstr+1, valname);
+
+ if (!jglb.is_enabled())
+ continue;
+
+ json::ref jrefi = jref["table"][ji++];
+ jrefi["offset"] = offset;
+ jrefi["name"] = valname;
+ jrefi["size"] = abs(size);
+ if (valid)
+ jrefi["value"] = val; // TODO: May be unsafe JSON int if size > 6
+
+ json::ref jreff = jrefi["flags"];
+ jreff["value"] = flags;
+ jreff["string"] = flagstr;
+ jreff["valid"] = valid;
+ jreff["normalized"] = normalized;
+ jreff["supports_dsn"] = supports_dsn;
+ jreff["monitored_condition_met"] = monitored_condition_met;
+ if (reserved_flags)
+ jreff["other"] = reserved_flags;
+
+ if (valid)
+ set_json_globals_from_device_statistics(page, offset, val);
}
}
else
rc = ataReadSmartLog(device, 0x04, page_0, 1);
if (!rc) {
- pout("Read Device Statistics page 0x00 failed\n\n");
+ jerr("Read Device Statistics page 0x00 failed\n\n");
return false;
}
unsigned char nentries = page_0[8];
if (!(page_0[2] == 0 && nentries > 0)) {
- pout("Device Statistics page 0x00 is invalid (page=0x%02x, nentries=%d)\n\n", page_0[2], nentries);
+ jerr("Device Statistics page 0x00 is invalid (page=0x%02x, nentries=%d)\n\n", page_0[2], nentries);
return false;
}
ssd_page = false;
}
+ json::ref jref = jglb["ata_device_statistics"];
+
// Print list of supported pages if requested
if (print_page_0) {
pout("Device Statistics (%s Log 0x04) supported pages\n",
use_gplog ? "GP" : "SMART");
- pout("Page Description\n");
+ jout("Page Description\n");
for (i = 0; i < nentries; i++) {
int page = page_0[8+1+i];
- pout("0x%02x %s\n", page, get_device_statistics_page_name(page));
+ const char * name = get_device_statistics_page_name(page);
+ jout("0x%02x %s\n", page, name);
+ jref["supported_pages"][i]["number"] = page;
+ jref["supported_pages"][i]["name"] = name;
}
- pout("\n");
+ jout("\n");
}
// Read & print pages
if (!pages.empty()) {
pout("Device Statistics (%s Log 0x04)\n",
use_gplog ? "GP" : "SMART");
- pout("Page Offset Size Value Flags Description\n");
+ jout("Page Offset Size Value Flags Description\n");
int max_page = 0;
if (!use_gplog)
raw_buffer pages_buf((max_page+1) * 512);
if (!use_gplog && !ataReadSmartLog(device, 0x04, pages_buf.data(), max_page+1)) {
- pout("Read Device Statistics pages 0x00-0x%02x failed\n\n", max_page);
+ jerr("Read Device Statistics pages 0x00-0x%02x failed\n\n", max_page);
return false;
}
+ int ji = 0;
for (i = 0; i < pages.size(); i++) {
int page = pages[i];
if (use_gplog) {
if (!ataReadLogExt(device, 0x04, 0, page, pages_buf.data(), 1)) {
- pout("Read Device Statistics page 0x%02x failed\n\n", page);
+ jerr("Read Device Statistics page 0x%02x failed\n\n", page);
return false;
}
}
continue;
int offset = (use_gplog ? 0 : page * 512);
- print_device_statistics_page(pages_buf.data() + offset, page);
+ print_device_statistics_page(jref["pages"][ji++], pages_buf.data() + offset, page);
}
- pout("%32s|||_ C monitored condition met\n", "");
- pout("%32s||__ D supports DSN\n", "");
- pout("%32s|___ N normalized value\n\n", "");
+ jout("%32s|||_ C monitored condition met\n", "");
+ jout("%32s||__ D supports DSN\n", "");
+ jout("%32s|___ N normalized value\n\n", "");
}
return true;
// Section 9.26 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017
-// TODO: Move to utility.h:
-static inline unsigned le32_to_uint(const unsigned char * val)
-{
- return ( (unsigned)val[0]
- | ((unsigned)val[1] << 8)
- | ((unsigned)val[2] << 16)
- | ((unsigned)val[3] << 24));
-}
-
-static inline uint64_t le64_to_uint(const unsigned char * val)
-{
- return (le32_to_uint(val) | ((uint64_t)le32_to_uint(val + 4) << 32));
-}
-
static bool print_pending_defects_log(ata_device * device, unsigned nsectors,
unsigned max_entries)
{
return false;
}
- pout("Pending Defects log (GP Log 0x0c)\n");
- unsigned nentries = le32_to_uint(page_buf);
+ jout("Pending Defects log (GP Log 0x0c)\n");
+ unsigned nentries = sg_get_unaligned_le32(page_buf);
+ json::ref jref = jglb["ata_pending_defects_log"];
+ jref["size"] = nsectors * 32 - 1;
+ jref["count"] = nentries;
if (!nentries) {
- pout("No Defects Logged\n\n");
+ jout("No Defects Logged\n\n");
return true;
}
// Print entries
- pout("Index LBA Hours\n");
+ jout("Index LBA Hours\n");
for (unsigned i = 0, pi = 1, page = 0; i < nentries && i < max_entries; i++, pi++) {
// Read new page if required
if (pi >= 32) {
}
const unsigned char * entry = page_buf + 16 * pi;
- unsigned hours = le32_to_uint(entry);
+ unsigned hours = sg_get_unaligned_le32(entry);
char hourstr[32];
if (hours != 0xffffffffU)
snprintf(hourstr, sizeof(hourstr), "%u", hours);
else
hourstr[0] = '-', hourstr[1] = 0;
- uint64_t lba = le64_to_uint(entry + 8);
- pout("%5u %18" PRIu64 " %8s\n", i, lba, hourstr);
+ uint64_t lba = sg_get_unaligned_le64(entry + 8);
+ jout("%5u %18" PRIu64 " %8s\n", i, lba, hourstr);
+
+ json::ref jrefi = jref["table"][i];
+ jrefi["lba"].set_unsafe_uint64(lba);
+ if (hours != 0xffffffffU)
+ jrefi["power_on_hours"] = hours;
}
if (nentries > max_entries)
pout("... (%u entries not shown)\n", nentries - max_entries);
- // TODO: Remove when no longer EXPERIMENTAL
- pout("Please send sample output of above table to:\n" PACKAGE_BUGREPORT "\n");
- pout("\n");
+ jout("\n");
return true;
}
{
if (checksum(data))
checksumwarning("SATA Phy Event Counters");
- pout("SATA Phy Event Counters (GP Log 0x11)\n");
+ jout("SATA Phy Event Counters (GP Log 0x11)\n");
if (data[0] || data[1] || data[2] || data[3])
pout("[Reserved: 0x%02x 0x%02x 0x%02x 0x%02x]\n",
data[0], data[1], data[2], data[3]);
- pout("ID Size Value Description\n");
+ jout("ID Size Value Description\n");
- for (unsigned i = 4; ; ) {
+ for (unsigned i = 4, ji = 0; ; ) {
// Get counter id and size (bits 14:12)
unsigned id = data[i] | (data[i+1] << 8);
unsigned size = ((id >> 12) & 0x7) << 1;
}
// Counters stop at max value, add '+' in this case
- pout("0x%04x %u %12" PRIu64 "%c %s\n", id, size, val,
+ jout("0x%04x %u %12" PRIu64 "%c %s\n", id, size, val,
(val == max_val ? '+' : ' '), name);
+
+ json::ref jref = jglb["sata_phy_event_counters"]["table"][ji++];
+ jref["id"] = id;
+ jref["name"] = name;
+ jref["size"] = size;
+ jref["value"] = val;
+ jref["overflow"] = (val == max_val);
}
if (reset)
- pout("All counters reset\n");
- pout("\n");
+ jout("All counters reset\n");
+ jout("\n");
+ jglb["sata_phy_event_counters"]["reset"] = reset;
}
// Format milliseconds from error log entry as "DAYS+H:M:S.MSEC"
static int PrintSmartErrorlog(const ata_smart_errorlog *data,
firmwarebug_defs firmwarebugs)
{
- pout("SMART Error Log Version: %d\n", (int)data->revnumber);
-
+ json::ref jref = jglb["ata_smart_error_log"]["summary"];
+ jout("SMART Error Log Version: %d\n", (int)data->revnumber);
+ jref["revision"] = data->revnumber;
+
// if no errors logged, return
if (!data->error_log_pointer){
- pout("No Errors Logged\n\n");
+ jout("No Errors Logged\n\n");
+ jref["count"] = 0;
return 0;
}
print_on();
// starting printing error log info
if (data->ata_error_count<=5)
- pout( "ATA Error Count: %d\n", (int)data->ata_error_count);
+ jout( "ATA Error Count: %d\n", (int)data->ata_error_count);
else
- pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
+ jout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
(int)data->ata_error_count);
+ jref["count"] = data->ata_error_count;
+ jref["logged_count"] = (data->ata_error_count <= 5 ? data->ata_error_count : 5);
+
print_off();
- pout("\tCR = Command Register [HEX]\n"
+ jout("\tCR = Command Register [HEX]\n"
"\tFR = Features Register [HEX]\n"
"\tSC = Sector Count Register [HEX]\n"
"\tSN = Sector Number Register [HEX]\n"
"SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n");
// now step through the five error log data structures (table 39 of spec)
- for (int k = 4; k >= 0; k-- ) {
+ for (int k = 4, ji = 0; k >= 0; k--) {
// The error log data structure entries are a circular buffer
int i = (data->error_log_pointer + k) % 5;
// See table 42 of ATA5 spec
print_on();
- pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n",
+ jout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n",
(int)(data->ata_error_count+k-4), (int)summary->timestamp, days, (int)(summary->timestamp-24*days));
print_off();
- pout(" When the command that caused the error occurred, the device was %s.\n\n",msgstate);
- pout(" After command completion occurred, registers were:\n"
+
+ json::ref jrefi = jref["table"][ji++];
+ jrefi["error_number"] = data->ata_error_count + k - 4;
+ jrefi["lifetime_hours"] = summary->timestamp;
+
+ jout(" When the command that caused the error occurred, the device was %s.\n\n", msgstate);
+ jout(" After command completion occurred, registers were:\n"
" ER ST SC SN CL CH DH\n"
" -- -- -- -- -- -- --\n"
" %02x %02x %02x %02x %02x %02x %02x",
(int)summary->cylinder_low,
(int)summary->cylinder_high,
(int)summary->drive_head);
+
+ {
+ json::ref jrefir = jrefi["completion_registers"];
+ jrefir["error"] = summary->error_register;
+ jrefir["status"] = summary->status;
+ jrefir["count"] = summary->sector_count;
+ jrefir["lba"] = (summary->sector_number )
+ | (summary->cylinder_low << 8)
+ | (summary->cylinder_high << 16);
+ jrefir["device"] = summary->drive_head;
+ }
+
// Add a description of the contents of the status and error registers
// if possible
std::string st_er_desc = format_st_er_desc(elog);
- if (!st_er_desc.empty())
- pout(" %s", st_er_desc.c_str());
- pout("\n\n");
- pout(" Commands leading to the command that caused the error were:\n"
+ if (!st_er_desc.empty()) {
+ jout(" %s", st_er_desc.c_str());
+ jrefi["error_description"] = st_er_desc;
+ }
+ jout("\n\n");
+ jout(" Commands leading to the command that caused the error were:\n"
" CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name\n"
" -- -- -- -- -- -- -- -- ---------------- --------------------\n");
- for (int j = 4; j >= 0; j--) {
+ for (int j = 4, jj = 0; j >= 0; j--) {
const ata_smart_errorlog_command_struct * thiscommand = elog->commands+j;
// Spec says: unused data command structures shall be zero filled
if (nonempty(thiscommand, sizeof(*thiscommand))) {
- pout(" %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n",
+ const char * atacmd = look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg);
+ jout(" %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n",
(int)thiscommand->commandreg,
(int)thiscommand->featuresreg,
(int)thiscommand->sector_count,
(int)thiscommand->drive_head,
(int)thiscommand->devicecontrolreg,
format_milliseconds(thiscommand->timestamp).c_str(),
- look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg));
- }
+ atacmd);
+
+ json::ref jrefic = jrefi["previous_commands"][jj++];
+ json::ref jreficr = jrefic["registers"];
+ jreficr["command"] = thiscommand->commandreg;
+ jreficr["features"] = thiscommand->featuresreg,
+ jreficr["count"] = thiscommand->sector_count;
+ jreficr["lba"] = (thiscommand->sector_number )
+ | (thiscommand->cylinder_low << 8)
+ | (thiscommand->cylinder_high << 16);
+ jreficr["device"] = thiscommand->drive_head;
+ jreficr["device_control"] = thiscommand->devicecontrolreg;
+ jrefic["powerup_milliseconds"] = thiscommand->timestamp;
+ jrefic["command_name"] = atacmd;
+ }
}
- pout("\n");
+ jout("\n");
}
}
print_on();
const ata_smart_exterrlog * log,
unsigned nsectors, unsigned max_errors)
{
- pout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n",
+ json::ref jref = jglb["ata_smart_error_log"]["extended"];
+ jout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n",
log->version, nsectors);
+ jref["revision"] = log->version;
+ jref["sectors"] = nsectors;
if (!log->device_error_count) {
- pout("No Errors Logged\n\n");
+ jout("No Errors Logged\n\n");
+ jref["count"] = 0;
return 0;
}
print_on();
unsigned errcnt = log->device_error_count;
if (errcnt <= nentries)
- pout("Device Error Count: %u\n", log->device_error_count);
+ jout("Device Error Count: %u\n", log->device_error_count);
else {
errcnt = nentries;
- pout("Device Error Count: %u (device log contains only the most recent %u errors)\n",
+ jout("Device Error Count: %u (device log contains only the most recent %u errors)\n",
log->device_error_count, errcnt);
}
+ jref["count"] = log->device_error_count;
+ jref["logged_count"] = errcnt;
if (max_errors < errcnt)
errcnt = max_errors;
print_off();
- pout("\tCR = Command Register\n"
+ jout("\tCR = Command Register\n"
"\tFEATR = Features Register\n"
"\tCOUNT = Count (was: Sector Count) Register\n"
"\tLBA_48 = Upper bytes of LBA High/Mid/Low Registers ] ATA-8\n"
const ata_smart_exterrlog_error_log & entry = log_p->error_logs[erridx % 4];
+ json::ref jrefi = jref["table"][i];
+ jrefi["error_number"] = errnum;
+ jrefi["log_index"] = erridx;
+
// Skip unused entries
if (!nonempty(&entry, sizeof(entry))) {
- pout("Error %u [%u] log entry is empty\n", errnum, erridx);
+ jout("Error %u [%u] log entry is empty\n", errnum, erridx);
continue;
}
// Print error information
print_on();
const ata_smart_exterrlog_error & err = entry.error;
- pout("Error %u [%u] occurred at disk power-on lifetime: %u hours (%u days + %u hours)\n",
+ jout("Error %u [%u] occurred at disk power-on lifetime: %u hours (%u days + %u hours)\n",
errnum, erridx, err.timestamp, err.timestamp / 24, err.timestamp % 24);
print_off();
+ jrefi["lifetime_hours"] = err.timestamp;
- pout(" When the command that caused the error occurred, the device was %s.\n\n",
- get_error_log_state_desc(err.state));
+ const char * msgstate = get_error_log_state_desc(err.state);
+ jout(" When the command that caused the error occurred, the device was %s.\n\n", msgstate);
+ jrefi["device_state"]["value"] = err.state;
+ jrefi["device_state"]["string"] = msgstate;
// Print registers
- pout(" After command completion occurred, registers were:\n"
+ jout(" After command completion occurred, registers were:\n"
" ER -- ST COUNT LBA_48 LH LM LL DV DC\n"
" -- -- -- == -- == == == -- -- -- -- --\n"
" %02x -- %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
err.device_register,
err.device_control_register);
+ {
+ json::ref jrefir = jrefi["completion_registers"];
+ jrefir["error"] = err.error_register;
+ jrefir["status"] = err.status_register,
+ jrefir["count"] = (err.count_register_hi << 8) | err.count_register;
+ jrefir["lba"] = ((uint64_t)err.lba_high_register_hi << 40)
+ | ((uint64_t)err.lba_mid_register_hi << 32)
+ | ((uint64_t)err.lba_low_register_hi << 24)
+ | ((unsigned)err.lba_high_register << 16)
+ | ((unsigned)err.lba_mid_register << 8)
+ | ((unsigned)err.lba_low_register );
+ jrefir["device"] = err.device_register;
+ jrefir["device_control"] = err.device_control_register;
+ }
+
// Add a description of the contents of the status and error registers
// if possible
std::string st_er_desc = format_st_er_desc(&entry);
- if (!st_er_desc.empty())
- pout(" %s", st_er_desc.c_str());
- pout("\n\n");
+ if (!st_er_desc.empty()) {
+ jout(" %s", st_er_desc.c_str());
+ jrefi["error_description"] = st_er_desc;
+ }
+ jout("\n\n");
// Print command history
- pout(" Commands leading to the command that caused the error were:\n"
+ jout(" Commands leading to the command that caused the error were:\n"
" CR FEATR COUNT LBA_48 LH LM LL DV DC Powered_Up_Time Command/Feature_Name\n"
" -- == -- == -- == == == -- -- -- -- -- --------------- --------------------\n");
- for (int ci = 4; ci >= 0; ci--) {
+ for (int ci = 4, cji = 0; ci >= 0; ci--) {
const ata_smart_exterrlog_command & cmd = entry.commands[ci];
// Skip unused entries
continue;
// Print registers, timestamp and ATA command name
- pout(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n",
+ const char * atacmd = look_up_ata_command(cmd.command_register, cmd.features_register);
+ jout(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s %s\n",
cmd.command_register,
cmd.features_register_hi,
cmd.features_register,
cmd.device_register,
cmd.device_control_register,
format_milliseconds(cmd.timestamp).c_str(),
- look_up_ata_command(cmd.command_register, cmd.features_register));
+ atacmd);
+
+ json::ref jrefic = jrefi["previous_commands"][cji++];
+ json::ref jreficr = jrefic["registers"];
+ jreficr["command"] = cmd.command_register;
+ jreficr["features"] = (cmd.features_register_hi << 8) | cmd.features_register;
+ jreficr["count"] = (cmd.count_register_hi << 8) | cmd.count_register;
+ jreficr["lba"] = ((uint64_t)cmd.lba_high_register_hi << 40)
+ | ((uint64_t)cmd.lba_mid_register_hi << 32)
+ | ((uint64_t)cmd.lba_low_register_hi << 24)
+ | ((unsigned)cmd.lba_high_register << 16)
+ | ((unsigned)cmd.lba_mid_register << 8)
+ | ((unsigned)cmd.lba_low_register );
+ jreficr["device"] = cmd.device_register;
+ jreficr["device_control"] = cmd.device_control_register;
+ jrefic["powerup_milliseconds"] = cmd.timestamp;
+ jrefic["command_name"] = atacmd;
}
- pout("\n");
+ jout("\n");
}
print_on();
return log->device_error_count;
}
+// Print one self-test log entry.
+// Returns:
+// -1: self-test failed
+// 1: extended self-test completed without error
+// 0: otherwise
+static int ataPrintSmartSelfTestEntry(const json::ref & jref,
+ unsigned testnum, unsigned char test_type,
+ unsigned char test_status,
+ unsigned short timestamp,
+ uint64_t failing_lba,
+ bool print_error_only, bool & print_header)
+{
+ // Check status and type for return value
+ int retval = 0;
+ switch (test_status >> 4) {
+ case 0x0:
+ if ((test_type & 0x7f) == 0x02)
+ retval = 1; // extended self-test completed without error
+ break;
+ case 0x3: case 0x4:
+ case 0x5: case 0x6:
+ case 0x7: case 0x8:
+ retval = -1; // self-test failed
+ break;
+ }
+
+ if (retval >= 0 && print_error_only)
+ return retval;
+
+ std::string msgtest;
+ switch (test_type) {
+ case 0x00: msgtest = "Offline"; break;
+ case 0x01: msgtest = "Short offline"; break;
+ case 0x02: msgtest = "Extended offline"; break;
+ case 0x03: msgtest = "Conveyance offline"; break;
+ case 0x04: msgtest = "Selective offline"; break;
+ case 0x7f: msgtest = "Abort offline test"; break;
+ case 0x81: msgtest = "Short captive"; break;
+ case 0x82: msgtest = "Extended captive"; break;
+ case 0x83: msgtest = "Conveyance captive"; break;
+ case 0x84: msgtest = "Selective captive"; break;
+ default:
+ if ((0x40 <= test_type && test_type <= 0x7e) || 0x90 <= test_type)
+ msgtest = strprintf("Vendor (0x%02x)", test_type);
+ else
+ msgtest = strprintf("Reserved (0x%02x)", test_type);
+ }
+
+ std::string msgstat;
+ switch (test_status >> 4) {
+ case 0x0: msgstat = "Completed without error"; break;
+ case 0x1: msgstat = "Aborted by host"; break;
+ case 0x2: msgstat = "Interrupted (host reset)"; break;
+ case 0x3: msgstat = "Fatal or unknown error"; break;
+ case 0x4: msgstat = "Completed: unknown failure"; break;
+ case 0x5: msgstat = "Completed: electrical failure"; break;
+ case 0x6: msgstat = "Completed: servo/seek failure"; break;
+ case 0x7: msgstat = "Completed: read failure"; break;
+ case 0x8: msgstat = "Completed: handling damage??"; break;
+ case 0xf: msgstat = "Self-test routine in progress"; break;
+ default: msgstat = strprintf("Unknown status (0x%x)", test_status >> 4);
+ }
+
+ // Print header once
+ if (print_header) {
+ print_header = false;
+ jout("Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error\n");
+ }
+
+ char msglba[32];
+ if (retval < 0 && failing_lba < 0xffffffffffffULL)
+ snprintf(msglba, sizeof(msglba), "%" PRIu64, failing_lba);
+ else {
+ msglba[0] = '-'; msglba[1] = 0;
+ }
+
+ jout("#%2u %-19s %-29s %1d0%% %8u %s\n", testnum,
+ msgtest.c_str(), msgstat.c_str(), test_status & 0x0f, timestamp, msglba);
+
+ jref["type"]["value"] = test_type;
+ jref["type"]["string"] = msgtest;
+
+ jref["status"]["value"] = test_status;
+ jref["status"]["string"] = msgstat;
+ if (test_status & 0x0f)
+ jref["status"]["remaining_percent"] = (test_status & 0x0f) * 10;
+ switch (test_status >> 4) {
+ case 0x1: case 0x2: case 0x3: break; // aborted -> unknown
+ default: jref["status"]["passed"] = (retval >= 0);
+ }
+
+ jref["lifetime_hours"] = timestamp;
+
+ if (retval < 0 && failing_lba < 0xffffffffffffULL)
+ jref["lba"] = failing_lba;
+
+ return retval;
+}
+
+// Print SMART Self-test log, return error count
+static int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * log, bool allentries,
+ firmwarebug_defs firmwarebugs)
+{
+ json::ref jref = jglb["ata_smart_self_test_log"]["standard"];
+
+ if (allentries)
+ jout("SMART Self-test log structure revision number %d\n", log->revnumber);
+ jref["revision"] = log->revnumber;
+ if (log->revnumber != 0x0001 && allentries && !firmwarebugs.is_set(BUG_SAMSUNG))
+ pout("Warning: ATA Specification requires self-test log structure revision number = 1\n");
+ if (!log->mostrecenttest){
+ if (allentries)
+ jout("No self-tests have been logged. [To run self-tests, use: smartctl -t]\n");
+ jref["count"] = 0;
+ return 0;
+ }
+
+ bool noheaderprinted = true;
+ int errcnt = 0, igncnt = 0;
+ int testnum = 1, ext_ok_testnum = -1;
+
+ // Iterate through circular buffer in reverse direction
+ for (int i = 20, ji = 0; i >= 0; i--) {
+ int j = (i + log->mostrecenttest) % 21;
+ const ata_smart_selftestlog_struct & entry = log->selftest_struct[j];
+
+ // Skip unused entries
+ if (!nonempty(&entry, sizeof(entry)))
+ continue;
+
+ // Get LBA if valid
+ uint64_t lba48 = (entry.lbafirstfailure < 0xffffffff ?
+ entry.lbafirstfailure : 0xffffffffffffULL);
+
+ // Print entry
+ int state = ataPrintSmartSelfTestEntry(jref["table"][ji++],
+ testnum, entry.selftestnumber, entry.selfteststatus,
+ entry.timestamp, lba48, !allentries, noheaderprinted);
+
+ if (state < 0) {
+ // Self-test showed an error
+ if (ext_ok_testnum < 0)
+ errcnt++;
+ else
+ // Newer successful extended self-test exits
+ igncnt++;
+ }
+ else if (state > 0 && ext_ok_testnum < 0) {
+ // Latest successful extended self-test
+ ext_ok_testnum = testnum;
+ }
+ testnum++;
+ }
+
+ if (igncnt)
+ jout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n",
+ igncnt, igncnt+errcnt, ext_ok_testnum);
+ jref["count"] = testnum - 1;
+ jref["error_count_total"] = igncnt + errcnt;
+ jref["error_count_outdated"] = igncnt;
+
+ if (!allentries && !noheaderprinted)
+ jout("\n");
+
+ return errcnt;
+}
+
// Print SMART Extended Self-test Log (GP Log 0x07)
static int PrintSmartExtSelfTestLog(const ata_smart_extselftestlog * log,
unsigned nsectors, unsigned max_entries)
{
- pout("SMART Extended Self-test Log Version: %u (%u sectors)\n",
+ json::ref jref = jglb["ata_smart_self_test_log"]["extended"];
+
+ jout("SMART Extended Self-test Log Version: %u (%u sectors)\n",
log->version, nsectors);
+ jref["revision"] = log->version;
+ jref["sectors"] = nsectors;
if (!log->log_desc_index){
- pout("No self-tests have been logged. [To run self-tests, use: smartctl -t]\n\n");
+ jout("No self-tests have been logged. [To run self-tests, use: smartctl -t]\n\n");
+ jref["count"] = 0;
return 0;
}
bool print_header = true;
int errcnt = 0, igncnt = 0;
int ext_ok_testnum = -1;
+ unsigned testnum = 1;
// Iterate through circular buffer in reverse direction
- for (unsigned i = 0, testnum = 1;
- i < nentries && testnum <= max_entries;
+ for (unsigned i = 0, ji = 0; i < nentries && testnum <= max_entries;
i++, logidx = (logidx > 0 ? logidx - 1 : nentries - 1)) {
const ata_smart_extselftestlog_desc & entry = log[logidx / 19].log_descs[logidx % 19];
| ((uint64_t)b[5] << 40);
// Print entry
- int state = ataPrintSmartSelfTestEntry(testnum, entry.self_test_type,
+ int state = ataPrintSmartSelfTestEntry(jref["table"][ji++],
+ testnum, entry.self_test_type,
entry.self_test_status, entry.timestamp, lba48,
false /*!print_error_only*/, print_header);
}
if (igncnt)
- pout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n",
+ jout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n",
igncnt, igncnt+errcnt, ext_ok_testnum);
+ jref["count"] = testnum - 1;
+ jref["error_count_total"] = igncnt + errcnt;
+ jref["error_count_outdated"] = igncnt;
- pout("\n");
+ jout("\n");
return errcnt;
}
static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log, const ata_smart_values * sv)
{
- int i,field1,field2;
- const char *msg;
- char tmp[64];
- uint64_t maxl=0,maxr=0;
- uint64_t current=log->currentlba;
- uint64_t currentend=current+65535;
+ json::ref jref = jglb["ata_smart_selective_self_test_log"];
// print data structure revision number
- pout("SMART Selective self-test log data structure revision number %d\n",(int)log->logversion);
+ jout("SMART Selective self-test log data structure revision number %d\n", log->logversion);
+ jref["revision"] = log->logversion;
if (1 != log->logversion)
pout("Note: revision number not 1 implies that no selective self-test has ever been run\n");
+ const char *msg;
switch((sv->self_test_exec_status)>>4){
case 0:msg="Completed";
break;
// find the number of columns needed for printing. If in use, the
// start/end of span being read-scanned...
+ uint64_t maxl = 0, maxr = 0;
+ uint64_t current = log->currentlba;
+ uint64_t currentend = current + 0xffff;
if (log->currentspan>5) {
maxl=current;
maxr=currentend;
}
- for (i=0; i<5; i++) {
+ for (int i = 0; i < 5; i++) {
uint64_t start=log->span[i].start;
uint64_t end =log->span[i].end;
// ... plus max start/end of each of the five test spans.
maxr=end;
}
- // we need at least 7 characters wide fields to accomodate the
+ // we need at least 7 characters wide fields to accommodate the
// labels
+ int field1,field2;
+ char tmp[64];
if ((field1=snprintf(tmp,64, "%" PRIu64, maxl))<7)
field1=7;
if ((field2=snprintf(tmp,64, "%" PRIu64, maxr))<7)
field2=7;
// now print the five test spans
- pout(" SPAN %*s %*s CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA");
+ jout(" SPAN %*s %*s CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA");
- for (i=0; i<5; i++) {
+ for (int i = 0; i < 5; i++) {
uint64_t start=log->span[i].start;
uint64_t end=log->span[i].end;
-
- if ((i+1)==(int)log->currentspan)
+ bool active = (i + 1 == log->currentspan);
+
+ if (active)
// this span is currently under test
- pout(" %d %*" PRIu64 " %*" PRIu64 " %s [%01d0%% left] (%" PRIu64 "-%" PRIu64 ")\n",
- i+1, field1, start, field2, end, msg,
- (int)(sv->self_test_exec_status & 0xf), current, currentend);
+ jout(" %d %*" PRIu64 " %*" PRIu64 " %s [%01d0%% left] (%" PRIu64 "-%" PRIu64 ")\n",
+ i + 1, field1, start, field2, end, msg,
+ (sv->self_test_exec_status & 0xf), current, currentend);
else
// this span is not currently under test
- pout(" %d %*" PRIu64 " %*" PRIu64 " Not_testing\n",
- i+1, field1, start, field2, end);
- }
-
+ jout(" %d %*" PRIu64 " %*" PRIu64 " Not_testing\n",
+ i + 1, field1, start, field2, end);
+
+ json::ref jrefi = jref["table"][i];
+ jrefi["lba_min"] = start;
+ jrefi["lba_max"] = end;
+ jrefi["status"]["value"] = sv->self_test_exec_status;
+ jrefi["status"]["string"] = (active ? msg : "Not_testing");
+ if (active) {
+ jrefi["status"]["remaining_percent"] = sv->self_test_exec_status & 0xf;
+ jrefi["current_lba_min"] = current;
+ jrefi["current_lba_max"] = currentend;
+ }
+ }
+
// if we are currently read-scanning, print LBAs and the status of
// the read scan
- if (log->currentspan>5)
- pout("%5d %*" PRIu64 " %*" PRIu64 " Read_scanning %s\n",
- (int)log->currentspan, field1, current, field2, currentend,
- OfflineDataCollectionStatus(sv->offline_data_collection_status));
+ if (log->currentspan > 5) {
+ const char * ost = OfflineDataCollectionStatus(sv->offline_data_collection_status);
+ jout("%5d %*" PRIu64 " %*" PRIu64 " Read_scanning %s\n",
+ log->currentspan, field1, current, field2, currentend, ost);
+ json::ref jrefc = jref["current_read_scan"];
+ jrefc["lba_min"] = current;
+ jrefc["lba_max"] = currentend;
+ jrefc["status"]["value"] = sv->offline_data_collection_status;
+ jrefc["status"]["string"] = ost;
+ }
/* Print selective self-test flags. Possible flag combinations are
(numbering bits from 0-15):
1 1 1 Currently scanning
*/
- pout("Selective self-test flags (0x%x):\n", (unsigned int)log->flags);
+ jout("Selective self-test flags (0x%x):\n", (unsigned)log->flags);
+ json::ref jreff = jref["flags"];
+ jreff["value"] = log->flags;
+ jreff["remainder_scan_enabled"] = !!(log->flags & SELECTIVE_FLAG_DOSCAN);
if (log->flags & SELECTIVE_FLAG_DOSCAN) {
- if (log->flags & SELECTIVE_FLAG_ACTIVE)
- pout(" Currently read-scanning the remainder of the disk.\n");
- else if (log->flags & SELECTIVE_FLAG_PENDING)
- pout(" Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n",
- (int)log->pendingtime);
- else
- pout(" After scanning selected spans, read-scan remainder of disk.\n");
+ if (log->flags & SELECTIVE_FLAG_ACTIVE)
+ jout(" Currently read-scanning the remainder of the disk.\n");
+ else if (log->flags & SELECTIVE_FLAG_PENDING)
+ jout(" Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n",
+ log->pendingtime);
+ else
+ jout(" After scanning selected spans, read-scan remainder of disk.\n");
+ jreff["remainder_scan_active"] = !!(log->flags & SELECTIVE_FLAG_ACTIVE);
+ jreff["power_up_scan_pending"] = !!(log->flags & SELECTIVE_FLAG_PENDING);
}
else
- pout(" After scanning selected spans, do NOT read-scan remainder of disk.\n");
+ jout(" After scanning selected spans, do NOT read-scan remainder of disk.\n");
// print pending time
- pout("If Selective self-test is pending on power-up, resume after %d minute delay.\n",
- (int)log->pendingtime);
-
- return;
+ jout("If Selective self-test is pending on power-up, resume after %d minute delay.\n",
+ log->pendingtime);
+ jref["power_up_scan_resume_minutes"] = log->pendingtime;
}
// Format SCT Temperature value
return buf;
}
+static void sct_jtemp2(const json::ref & jref, const char * name, signed char x)
+{
+ if (x == -128 /*0x80 = unknown*/)
+ return;
+ jglb["temperature"][name] = x;
+ jref["temperature"][name] = x;
+}
+
static const char * sct_pbar(int x, char (& buf)[64])
{
if (x <= 19)
// Print SCT Status
static int ataPrintSCTStatus(const ata_sct_status_response * sts)
{
- pout("SCT Status Version: %u\n", sts->format_version);
- pout("SCT Version (vendor specific): %u (0x%04x)\n", sts->sct_version, sts->sct_version);
- pout("SCT Support Level: %u\n", sts->sct_spec);
- pout("Device State: %s (%u)\n",
- sct_device_state_msg(sts->device_state), sts->device_state);
+ json::ref jref = jglb["ata_sct_status"];
+
+ jout("SCT Status Version: %u\n", sts->format_version);
+ jref["format_version"] = sts->format_version;
+ jout("SCT Version (vendor specific): %u (0x%04x)\n", sts->sct_version, sts->sct_version);
+ jref["sct_version"] = sts->sct_version;
+ // SCT Support Level (1) from original SCT draft was later declared obsolete in ATA-8 ACS.
+ // Drives typically return 0 or 1. Print only if unknown value is returned.
+ if (sts->sct_spec > 1)
+ pout("SCT Support Level: %u\n", sts->sct_spec);
+ const char * statestr = sct_device_state_msg(sts->device_state);
+ jout("Device State: %s (%u)\n", statestr, sts->device_state);
+ jref["device_state"]["value"] = sts->device_state;
+ jref["device_state"]["string"] = statestr;
+
+ // If "Reserved" fields not set, assume "old" format version 2:
+ // Table 11 of T13/1701DT-N (SMART Command Transport) Revision 5, February 2005
+ // Table 54 of T13/1699-D (ATA8-ACS) Revision 3e, July 2006
+ // ... else assume "new" format version 2 or version 3:
+ // T13/e06152r0-3 (Additional SCT Temperature Statistics), August - October 2006
+ // Table 60 of T13/1699-D (ATA8-ACS) Revision 3f, December 2006 (format version 2)
+ // Table 80 of T13/1699-D (ATA8-ACS) Revision 6a, September 2008 (format version 3)
+ // Table 194 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017
+ // (max_op_limit, smart_status, min_erc_time)
+ bool old_format_2 = ( !sts->min_temp && !sts->life_min_temp
+ && !sts->under_limit_count && !sts->over_limit_count);
+
char buf1[20], buf2[20];
- if ( !sts->min_temp && !sts->life_min_temp
- && !sts->under_limit_count && !sts->over_limit_count) {
- // "Reserved" fields not set, assume "old" format version 2
- // Table 11 of T13/1701DT-N (SMART Command Transport) Revision 5, February 2005
- // Table 54 of T13/1699-D (ATA8-ACS) Revision 3e, July 2006
- pout("Current Temperature: %s Celsius\n",
- sct_ptemp(sts->hda_temp, buf1));
- pout("Power Cycle Max Temperature: %s Celsius\n",
- sct_ptemp(sts->max_temp, buf2));
- pout("Lifetime Max Temperature: %s Celsius\n",
- sct_ptemp(sts->life_max_temp, buf2));
+ jout("Current Temperature: %s Celsius\n",
+ sct_ptemp(sts->hda_temp, buf1));
+ sct_jtemp2(jref, "current", sts->hda_temp);
+ jout("Power Cycle Min/Max Temperature: %s/%s Celsius\n",
+ (!old_format_2 ? sct_ptemp(sts->min_temp, buf1) : "--"),
+ sct_ptemp(sts->max_temp, buf2));
+ if (!old_format_2)
+ sct_jtemp2(jref, "power_cycle_min", sts->min_temp);
+ sct_jtemp2(jref, "power_cycle_max", sts->max_temp);
+ jout("Lifetime Min/Max Temperature: %s/%s Celsius\n",
+ (!old_format_2 ? sct_ptemp(sts->life_min_temp, buf1) : "--"),
+ sct_ptemp(sts->life_max_temp, buf2));
+ if (!old_format_2)
+ sct_jtemp2(jref, "lifetime_min", sts->life_min_temp);
+ sct_jtemp2(jref, "lifetime_max", sts->life_max_temp);
+ if (old_format_2)
+ return 0;
+
+ if (sts->max_op_limit > 0) { // e06152r0-2: "Average Temperature"
+ jout("Specified Max Operating Temperature: %3d Celsius\n", sts->max_op_limit);
+ sct_jtemp2(jref, "op_limit_max", sts->max_op_limit);
}
- else {
- // Assume "new" format version 2 or version 3
- // T13/e06152r0-3 (Additional SCT Temperature Statistics), August - October 2006
- // Table 60 of T13/1699-D (ATA8-ACS) Revision 3f, December 2006 (format version 2)
- // Table 80 of T13/1699-D (ATA8-ACS) Revision 6a, September 2008 (format version 3)
- // Table 185 of T13/BSR INCITS 529 (ACS-4) Revision 16, February 21, 2017
- // (smart_status, min_erc_time)
- pout("Current Temperature: %s Celsius\n",
- sct_ptemp(sts->hda_temp, buf1));
- pout("Power Cycle Min/Max Temperature: %s/%s Celsius\n",
- sct_ptemp(sts->min_temp, buf1), sct_ptemp(sts->max_temp, buf2));
- pout("Lifetime Min/Max Temperature: %s/%s Celsius\n",
- sct_ptemp(sts->life_min_temp, buf1), sct_ptemp(sts->life_max_temp, buf2));
- signed char avg = sts->byte205; // Average Temperature from e06152r0-2, removed in e06152r3
- if (0 < avg && sts->life_min_temp <= avg && avg <= sts->life_max_temp)
- pout("Lifetime Average Temperature: %2d Celsius\n", avg);
- pout("Under/Over Temperature Limit Count: %2u/%u\n",
- sts->under_limit_count, sts->over_limit_count);
-
- if (sts->smart_status) // ACS-4
- pout("SMART Status: 0x%04x (%s)\n", sts->smart_status,
- (sts->smart_status == 0x2cf4 ? "FAILED" :
- sts->smart_status == 0xc24f ? "PASSED" : "Reserved"));
-
- if (sts->min_erc_time) // ACS-4
- pout("Minimum supported ERC Time Limit: %d (%0.1f seconds)\n",
- sts->min_erc_time, sts->min_erc_time/10.0);
-
- if (nonempty(sts->vendor_specific, sizeof(sts->vendor_specific))) {
- pout("Vendor specific:\n");
- for (unsigned i = 0; i < sizeof(sts->vendor_specific); i++)
- pout("%02x%c", sts->vendor_specific[i], ((i & 0xf) != 0xf ? ' ' : '\n'));
+ jout("Under/Over Temperature Limit Count: %2u/%u\n",
+ sts->under_limit_count, sts->over_limit_count);
+ jref["temperature"]["under_limit_count"] = sts->under_limit_count;
+ jref["temperature"]["over_limit_count"] = sts->over_limit_count;
+
+ if (sts->smart_status) { // ACS-4
+ int passed = (sts->smart_status == 0x2cf4 ? 0 :
+ sts->smart_status == 0xc24f ? 1 : -1);
+ jout("SMART Status: 0x%04x (%s)\n", sts->smart_status,
+ (passed == 0 ? "FAILED" : passed > 0 ? "PASSED" : "Reserved"));
+ if (passed >= 0) {
+ jref["smart_status"]["passed"] = !!passed;
+ jglb["smart_status"]["passed"] = !!passed;
+ }
+ else
+ jref["smart_status"]["reserved_value"] = sts->smart_status;
+ }
+
+ if (sts->min_erc_time) // ACS-4
+ pout("Minimum supported ERC Time Limit: %d (%0.1f seconds)\n",
+ sts->min_erc_time, sts->min_erc_time/10.0);
+
+ if (nonempty(sts->vendor_specific, sizeof(sts->vendor_specific))) {
+ jout("Vendor specific:\n");
+ for (unsigned i = 0; i < sizeof(sts->vendor_specific); i++) {
+ jout("%02x%c", sts->vendor_specific[i], ((i & 0xf) != 0xf ? ' ' : '\n'));
+ jref["vendor_specific"][i] = sts->vendor_specific[i];
}
}
return 0;
// Print SCT Temperature History Table
static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh)
{
+ json::ref jref = jglb["ata_sct_temperature_history"];
+
char buf1[20], buf2[20], buf3[64];
- pout("SCT Temperature History Version: %u%s\n", tmh->format_version,
+ jout("SCT Temperature History Version: %u%s\n", tmh->format_version,
(tmh->format_version != 2 ? " (Unknown, should be 2)" : ""));
- pout("Temperature Sampling Period: %u minute%s\n",
+ jref["version"] = tmh->format_version;
+ jout("Temperature Sampling Period: %u minute%s\n",
tmh->sampling_period, (tmh->sampling_period==1?"":"s"));
- pout("Temperature Logging Interval: %u minute%s\n",
+ jref["sampling_period_minutes"] = tmh->sampling_period;
+ jout("Temperature Logging Interval: %u minute%s\n",
tmh->interval, (tmh->interval==1?"":"s"));
- pout("Min/Max recommended Temperature: %s/%s Celsius\n",
+ jref["logging_interval_minutes"] = tmh->interval;
+
+ jout("Min/Max recommended Temperature: %s/%s Celsius\n",
sct_ptemp(tmh->min_op_limit, buf1), sct_ptemp(tmh->max_op_limit, buf2));
- pout("Min/Max Temperature Limit: %s/%s Celsius\n",
+ sct_jtemp2(jref, "op_limit_min", tmh->min_op_limit);
+ sct_jtemp2(jref, "op_limit_max", tmh->max_op_limit);
+ jout("Min/Max Temperature Limit: %s/%s Celsius\n",
sct_ptemp(tmh->under_limit, buf1), sct_ptemp(tmh->over_limit, buf2));
- pout("Temperature History Size (Index): %u (%u)\n", tmh->cb_size, tmh->cb_index);
+ sct_jtemp2(jref, "limit_min", tmh->under_limit);
+ sct_jtemp2(jref, "limit_max", tmh->over_limit);
+ jout("Temperature History Size (Index): %u (%u)\n", tmh->cb_size, tmh->cb_index);
+ jref["size"] = tmh->cb_size;
+ jref["index"] = tmh->cb_index;
if (!(0 < tmh->cb_size && tmh->cb_size <= sizeof(tmh->cb) && tmh->cb_index < tmh->cb_size)) {
if (!tmh->cb_size)
}
// Print table
- pout("\nIndex Estimated Time Temperature Celsius\n");
+ jout("\nIndex Estimated Time Temperature Celsius\n");
unsigned n = 0, i = (tmh->cb_index+1) % tmh->cb_size;
unsigned interval = (tmh->interval > 0 ? tmh->interval : 1);
time_t t = time(0) - (tmh->cb_size-1) * interval * 60;
char date[30];
// TODO: Don't print times < boot time
strftime(date, sizeof(date), "%Y-%m-%d %H:%M", localtime(&t));
- pout(" %3u %s %s %s\n", i, date,
+ jout(" %3u %s %s %s\n", i, date,
sct_ptemp(tmh->cb[i], buf1), sct_pbar(tmh->cb[i], buf3));
}
else if (n == n1+1) {
- pout(" ... ..(%3u skipped). .. %s\n",
+ jout(" ... ..(%3u skipped). .. %s\n",
n2-n1-2, sct_pbar(tmh->cb[i], buf3));
}
+ if (tmh->cb[i] != -128)
+ jref["table"][n] = tmh->cb[i];
t += interval * 60; i = (i+1) % tmh->cb_size; n++;
}
}
// Print SCT Error Recovery Control timers
static void ataPrintSCTErrorRecoveryControl(bool set, unsigned short read_timer, unsigned short write_timer)
{
- pout("SCT Error Recovery Control%s:\n", (set ? " set to" : ""));
+ json::ref jref = jglb["ata_sct_erc"];
+ jout("SCT Error Recovery Control%s:\n", (set ? " set to" : ""));
+
+ jref["read"]["enabled"] = !!read_timer;
if (!read_timer)
- pout(" Read: Disabled\n");
- else
- pout(" Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0);
+ jout(" Read: Disabled\n");
+ else {
+ jout(" Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0);
+ jref["read"]["deciseconds"] = read_timer;
+ }
+
+ jref["write"]["enabled"] = !!write_timer;
if (!write_timer)
- pout(" Write: Disabled\n");
- else
- pout(" Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0);
+ jout(" Write: Disabled\n");
+ else {
+ jout(" Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0);
+ jref["write"]["deciseconds"] = write_timer;
+ }
}
static void print_aam_level(const char * msg, int level, int recommended = -1)
s = "reserved";
if (recommended >= 0)
- pout("%s%d (%s), recommended: %d\n", msg, level, s, recommended);
+ jout("%s%d (%s), recommended: %d\n", msg, level, s, recommended);
else
- pout("%s%d (%s)\n", msg, level, s);
+ jout("%s%d (%s)\n", msg, level, s);
+
+ json::ref jref = jglb["ata_aam"];
+ jref["enabled"] = true;
+ jref["level"] = level;
+ jref["string"] = s;
+ if (recommended >= 0)
+ jref["recommended_level"] = recommended;
}
static void print_apm_level(const char * msg, int level)
else
s = "maximum performance";
- pout("%s%d (%s)\n", msg, level, s);
+ jout("%s%d (%s)\n", msg, level, s);
+
+ json::ref jref = jglb["ata_apm"];
+ jref["enabled"] = true;
+ jref["level"] = level;
+ jref["string"] = s;
+ if (1 <= level && level <= 254) {
+ jref["max_performance"] = (level == 254);
+ jref["min_power"] = (level == 1 || level == 128);
+ jref["with_standby"] = (level < 128);
+ }
}
static void print_ata_security_status(const char * msg, unsigned short state)
{
- const char * s1, * s2 = "", * s3 = "", * s4 = "";
+ // Table 6 of T13/2015-D (ACS-2) Revision 7, June 22, 2011
+ if (!(state & 0x0001)) {
+ pout("%sUnavailable\n", msg);
+ return;
+ }
+
+ const char * s1, * s2 = "", * s3 = "", * s4 = "";
+ bool enabled = false, locked = false;
+ if (!(state & 0x0002)) {
+ s1 = "Disabled, ";
+ if (!(state & 0x0008))
+ s2 = "NOT FROZEN [SEC1]";
+ else
+ s2 = "frozen [SEC2]";
+ }
+ else {
+ enabled = true;
+ s1 = "ENABLED, PW level ";
+ if (!(state & 0x0100))
+ s2 = "HIGH";
+ else
+ s2 = "MAX";
- // Table 6 of T13/2015-D (ACS-2) Revision 7, June 22, 2011
- if (!(state & 0x0001))
- s1 = "Unavailable";
- else if (!(state & 0x0002)) {
- s1 = "Disabled, ";
+ if (!(state & 0x0004)) {
+ s3 = ", not locked, ";
if (!(state & 0x0008))
- s2 = "NOT FROZEN [SEC1]";
+ s4 = "not frozen [SEC5]";
else
- s2 = "frozen [SEC2]";
+ s4 = "frozen [SEC6]";
}
else {
- s1 = "ENABLED, PW level ";
- if (!(state & 0x0100))
- s2 = "HIGH";
- else
- s2 = "MAX";
-
- if (!(state & 0x0004)) {
- s3 = ", not locked, ";
- if (!(state & 0x0008))
- s4 = "not frozen [SEC5]";
- else
- s4 = "frozen [SEC6]";
- }
- else {
- s3 = ", **LOCKED** [SEC4]";
- if (state & 0x0010)
- s4 = ", PW ATTEMPTS EXCEEDED";
- }
+ locked = true;
+ s3 = ", **LOCKED** [SEC4]";
+ if (state & 0x0010)
+ s4 = ", PW ATTEMPTS EXCEEDED";
}
+ }
- pout("%s%s%s%s%s\n", msg, s1, s2, s3, s4);
+ jout("%s%s%s%s%s\n", msg, s1, s2, s3, s4);
+
+ json::ref jref = jglb["ata_security"];
+ jref["state"] = state;
+ jref["string"] = strprintf("%s%s%s%s", s1, s2, s3, s4);
+ jref["enabled"] = enabled;
+ if (!enabled || !locked)
+ jref["frozen"] = !!(state & 0x0008);
+ if (enabled) {
+ jref["pw_level_max"] = !!(state & 0x0100);
+ jref["locked"] = locked;
+ if (locked)
+ jref["pw_attempts_exceeded"] = !!(state & 0x0010);
+ }
}
static void print_standby_timer(const char * msg, int timer, const ata_identify_device & drive)
}
if (powername) {
if (options.powermode >= powerlimit) {
- pout("Device is in %s mode, exit(%d)\n", powername, options.powerexit);
+ jinf("Device is in %s mode, exit(%d)\n", powername, options.powerexit);
return options.powerexit;
}
powerchg = (powermode != 0xff); // SMART tests will spin up drives
if (options.get_aam) {
if ((drive.command_set_2 & 0xc200) != 0x4200) // word083
pout("AAM feature is: Unavailable\n");
- else if (!(drive.word086 & 0x0200))
- pout("AAM feature is: Disabled\n");
+ else if (!(drive.word086 & 0x0200)) {
+ jout("AAM feature is: Disabled\n");
+ jglb["ata_aam"]["enabled"] = false;
+ }
else
print_aam_level("AAM level is: ", drive.words088_255[94-88] & 0xff,
drive.words088_255[94-88] >> 8);
if (options.get_apm) {
if ((drive.command_set_2 & 0xc008) != 0x4008) // word083
pout("APM feature is: Unavailable\n");
- else if (!(drive.word086 & 0x0008))
- pout("APM feature is: Disabled\n");
+ else if (!(drive.word086 & 0x0008)) {
+ jout("APM feature is: Disabled\n");
+ jglb["ata_apm"]["enabled"] = false;
+ }
else
print_apm_level("APM level is: ", drive.words088_255[91-88] & 0xff);
}
// Print read look-ahead status
if (options.get_lookahead) {
- pout("Rd look-ahead is: %s\n",
- ( (drive.command_set_2 & 0xc000) != 0x4000 // word083
- || !(drive.command_set_1 & 0x0040)) ? "Unavailable" : // word082
- !(drive.cfs_enable_1 & 0x0040) ? "Disabled" : "Enabled"); // word085
+ if ( (drive.command_set_2 & 0xc000) != 0x4000 // word083
+ || !(drive.command_set_1 & 0x0040) ) // word082
+ pout("Rd look-ahead is: Unavailable\n");
+ else {
+ bool enabled = !!(drive.cfs_enable_1 & 0x0040); // word085
+ jout("Rd look-ahead is: %sabled\n", (enabled ? "En" : "Dis"));
+ jglb["read_lookahead"]["enabled"] = enabled;
+ }
}
// Print write cache status
if (options.get_wcache) {
- pout("Write cache is: %s\n",
- ( (drive.command_set_2 & 0xc000) != 0x4000 // word083
- || !(drive.command_set_1 & 0x0020)) ? "Unavailable" : // word082
- !(drive.cfs_enable_1 & 0x0020) ? "Disabled" : "Enabled"); // word085
+ if ( (drive.command_set_2 & 0xc000) != 0x4000 // word083
+ || !(drive.command_set_1 & 0x0020) ) // word082
+ pout("Write cache is: Unavailable\n");
+ else {
+ bool enabled = !!(drive.cfs_enable_1 & 0x0020); // word085
+ jout("Write cache is: %sabled\n", (enabled ? "En" : "Dis"));
+ jglb["write_cache"]["enabled"] = enabled;
+ }
}
// Print DSN status
|| ((word119 & 0xc200) != 0x4200) // word119
|| ((word120 & 0xc000) != 0x4000)) // word120
pout("DSN feature is: Unavailable\n");
- else if (word120 & 0x200) // word120
- pout("DSN feature is: Enabled\n");
- else
- pout("DSN feature is: Disabled\n");
+ else {
+ bool enabled = !!(word120 & 0x200);
+ jout("DSN feature is: %sabled\n", (enabled ? "En" : "Dis"));
+ jglb["ata_dsn"]["enabled"] = enabled;
+ }
}
// Check for ATA Security LOCK
case 0:
// The case where the disk health is OK
- pout("SMART overall-health self-assessment test result: PASSED\n");
+ jout("SMART overall-health self-assessment test result: PASSED\n");
+ jglb["smart_status"]["passed"] = true;
if (smart_thres_ok && find_failed_attr(&smartval, &smartthres, attribute_defs, 0)) {
if (options.smart_vendor_attrib)
pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
case 1:
// The case where the disk health is NOT OK
print_on();
- pout("SMART overall-health self-assessment test result: FAILED!\n"
+ jout("SMART overall-health self-assessment test result: FAILED!\n"
"Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
+ jglb["smart_status"]["passed"] = false;
print_off();
if (smart_thres_ok && find_failed_attr(&smartval, &smartthres, attribute_defs, 1)) {
returnval|=FAILATTR;
}
else if (find_failed_attr(&smartval, &smartthres, attribute_defs, 1)) {
print_on();
- pout("SMART overall-health self-assessment test result: FAILED!\n"
+ jout("SMART overall-health self-assessment test result: FAILED!\n"
"Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
- pout("Warning: This result is based on an Attribute check.\n");
+ jwrn("Warning: This result is based on an Attribute check.\n");
+ jglb["smart_status"]["passed"] = false;
print_off();
returnval|=FAILATTR;
returnval|=FAILSTATUS;
}
}
else {
- pout("SMART overall-health self-assessment test result: PASSED\n");
- pout("Warning: This result is based on an Attribute check.\n");
+ jout("SMART overall-health self-assessment test result: PASSED\n");
+ jwrn("Warning: This result is based on an Attribute check.\n");
+ jglb["smart_status"]["passed"] = true;
if (find_failed_attr(&smartval, &smartthres, attribute_defs, 0)) {
if (options.smart_vendor_attrib)
pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
// If GP Log is supported use smart log directory for
// error and selftest log support check.
- bool gp_log_supported = !!isGeneralPurposeLoggingCapable(&drive);
+ bool gp_log_supported = isGeneralPurposeLoggingCapable(&drive);
if ( gp_log_supported
&& ( options.smart_error_log || options.smart_selftest_log
|| options.retry_error_log || options.retry_selftest_log))
bool use_gplog = true;
unsigned nsectors = 0;
if (gplogdir)
- nsectors = GetNumLogSectors(gplogdir, 0x04, false);
+ nsectors = GetNumLogSectors(gplogdir, 0x04, true);
else if (smartlogdir){ // for systems without ATA_READ_LOG_EXT
nsectors = GetNumLogSectors(smartlogdir, 0x04, false);
use_gplog = false;
}
// Print Pending Defects log
- if (options.pending_defects_log || options.pending_defects_info) {
+ if (options.pending_defects_log) {
unsigned nsectors = GetNumLogSectors(gplogdir, 0x0c, true);
if (!nsectors)
pout("Pending Defects log (GP Log 0x0c) not supported\n\n");
- else if (!options.pending_defects_log) // TODO: Remove when no longer EXPERIMENTAL
- pout("Pending Defects log (GP Log 0x0c) supported [please try: '-l defects']\n\n");
else if (!print_pending_defects_log(device, nsectors, options.pending_defects_log))
failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
}