*
* Home page of code is: http://www.smartmontools.org
*
- * Copyright (C) 2016-17 Christian Franke
- *
- * 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/>.
+ * Copyright (C) 2016-18 Christian Franke
*
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
+
#include "nvmeprint.h"
-const char * nvmeprint_cvsid = "$Id: nvmeprint.cpp 4580 2017-11-03 19:41:14Z chrfranke $"
+const char * nvmeprint_cvsid = "$Id: nvmeprint.cpp 4859 2018-12-16 18:09:44Z chrfranke $"
NVMEPRINT_H_CVSID;
-#include "int64.h"
#include "utility.h"
#include "dev_interface.h"
#include "nvmecmds.h"
#include "atacmds.h" // dont_print_serial_number
#include "scsicmds.h" // dStrHex()
#include "smartctl.h"
+#include "sg_unaligned.h"
+
+#include <inttypes.h>
using namespace smartmontools;
}
}
else {
- // More than 64-bit, print approximate value, prepend ~ flag
- snprintf(str, sizeof(str), "~%.0f",
- hi * (0xffffffffffffffffULL + 1.0) + lo);
+ // More than 64-bit, prepend '~' flag on low precision
+ int i = 0;
+ if (uint128_to_str_precision_bits() < 128)
+ str[i++] = '~';
+ uint128_hilo_to_str(str + i, (int)sizeof(str) - i, hi, lo);
}
return str;
return le128_to_str(str, (lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits), 1);
}
+// Output capacity specified as 64bit LBA count to JSON
+static void lbacap_to_js(const json::ref & jref, uint64_t lba_cnt, int lba_bits)
+{
+ jref["blocks"].set_unsafe_uint64(lba_cnt);
+ jref["bytes"].set_unsafe_uint128((lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits));
+}
+
// Format a Kelvin temperature value in Celsius.
static const char * kelvin_to_str(char (& str)[64], int k)
{
return str;
}
-static inline unsigned le16_to_uint(const unsigned char (& val)[2])
-{
- return ((val[1] << 8) | val[0]);
-}
-
static void print_drive_info(const nvme_id_ctrl & id_ctrl, const nvme_id_ns & id_ns,
unsigned nsid, bool show_all)
{
char buf[64];
- pout("Model Number: %s\n", format_char_array(buf, id_ctrl.mn));
- if (!dont_print_serial_number)
- pout("Serial Number: %s\n", format_char_array(buf, id_ctrl.sn));
- pout("Firmware Version: %s\n", format_char_array(buf, id_ctrl.fr));
+ jout("Model Number: %s\n", format_char_array(buf, id_ctrl.mn));
+ jglb["model_name"] = buf;
+ if (!dont_print_serial_number) {
+ jout("Serial Number: %s\n", format_char_array(buf, id_ctrl.sn));
+ jglb["serial_number"] = buf;
+ }
+
+ jout("Firmware Version: %s\n", format_char_array(buf, id_ctrl.fr));
+ jglb["firmware_version"] = buf;
// Vendor and Subsystem IDs are usually equal
if (show_all || id_ctrl.vid != id_ctrl.ssvid) {
- pout("PCI Vendor ID: 0x%04x\n", id_ctrl.vid);
- pout("PCI Vendor Subsystem ID: 0x%04x\n", id_ctrl.ssvid);
+ jout("PCI Vendor ID: 0x%04x\n", id_ctrl.vid);
+ jout("PCI Vendor Subsystem ID: 0x%04x\n", id_ctrl.ssvid);
}
else {
- pout("PCI Vendor/Subsystem ID: 0x%04x\n", id_ctrl.vid);
+ jout("PCI Vendor/Subsystem ID: 0x%04x\n", id_ctrl.vid);
}
+ jglb["nvme_pci_vendor"]["id"] = id_ctrl.vid;
+ jglb["nvme_pci_vendor"]["subsystem_id"] = id_ctrl.ssvid;
- pout("IEEE OUI Identifier: 0x%02x%02x%02x\n",
+ jout("IEEE OUI Identifier: 0x%02x%02x%02x\n",
id_ctrl.ieee[2], id_ctrl.ieee[1], id_ctrl.ieee[0]);
+ jglb["nvme_ieee_oui_identifier"] = sg_get_unaligned_le(3, id_ctrl.ieee);
// Capacity info is optional for devices without namespace management
if (show_all || le128_is_non_zero(id_ctrl.tnvmcap) || le128_is_non_zero(id_ctrl.unvmcap)) {
- pout("Total NVM Capacity: %s\n", le128_to_str(buf, id_ctrl.tnvmcap, 1));
- pout("Unallocated NVM Capacity: %s\n", le128_to_str(buf, id_ctrl.unvmcap, 1));
+ jout("Total NVM Capacity: %s\n", le128_to_str(buf, id_ctrl.tnvmcap, 1));
+ jglb["nvme_total_capacity"].set_unsafe_le128(id_ctrl.tnvmcap);
+ jout("Unallocated NVM Capacity: %s\n", le128_to_str(buf, id_ctrl.unvmcap, 1));
+ jglb["nvme_unallocated_capacity"].set_unsafe_le128(id_ctrl.unvmcap);
}
- pout("Controller ID: %d\n", id_ctrl.cntlid);
+ jout("Controller ID: %d\n", id_ctrl.cntlid);
+ jglb["nvme_controller_id"] = id_ctrl.cntlid;
// Print namespace info if available
- pout("Number of Namespaces: %u\n", id_ctrl.nn);
+ jout("Number of Namespaces: %u\n", id_ctrl.nn);
+ jglb["nvme_number_of_namespaces"] = id_ctrl.nn;
if (nsid && id_ns.nsze) {
const char * align = &(" "[nsid < 10 ? 0 : (nsid < 100 ? 1 : 2)]);
int fmt_lba_bits = id_ns.lbaf[id_ns.flbas & 0xf].ds;
+ json::ref jrns = jglb["nvme_namespaces"][0];
+ jrns["id"] = nsid;
+
// Size and Capacity are equal if thin provisioning is not supported
if (show_all || id_ns.ncap != id_ns.nsze || (id_ns.nsfeat & 0x01)) {
- pout("Namespace %u Size: %s%s\n", nsid, align,
+ jout("Namespace %u Size: %s%s\n", nsid, align,
lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits));
- pout("Namespace %u Capacity: %s%s\n", nsid, align,
+ jout("Namespace %u Capacity: %s%s\n", nsid, align,
lbacap_to_str(buf, id_ns.ncap, fmt_lba_bits));
}
else {
- pout("Namespace %u Size/Capacity: %s%s\n", nsid, align,
+ jout("Namespace %u Size/Capacity: %s%s\n", nsid, align,
lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits));
}
+ lbacap_to_js(jrns["size"], id_ns.nsze, fmt_lba_bits);
+ lbacap_to_js(jrns["capacity"], id_ns.ncap, fmt_lba_bits);
+ lbacap_to_js(jglb["user_capacity"], id_ns.ncap, fmt_lba_bits); // TODO: use nsze?
+
// Utilization may be always equal to Capacity if thin provisioning is not supported
if (show_all || id_ns.nuse != id_ns.ncap || (id_ns.nsfeat & 0x01))
- pout("Namespace %u Utilization: %s%s\n", nsid, align,
+ jout("Namespace %u Utilization: %s%s\n", nsid, align,
lbacap_to_str(buf, id_ns.nuse, fmt_lba_bits));
+ lbacap_to_js(jrns["utilization"], id_ns.nuse, fmt_lba_bits);
- pout("Namespace %u Formatted LBA Size: %s%u\n", nsid, align, (1U << fmt_lba_bits));
+ jout("Namespace %u Formatted LBA Size: %s%u\n", nsid, align, (1U << fmt_lba_bits));
+ jrns["formatted_lba_size"] = (1U << fmt_lba_bits);
+ jglb["logical_block_size"] = (1U << fmt_lba_bits);
- if (show_all || nonempty(id_ns.eui64, sizeof(id_ns.eui64)))
- pout("Namespace %u IEEE EUI-64: %s%02x%02x%02x %02x%02x%02x%02x%02x\n",
+ if (show_all || nonempty(id_ns.eui64, sizeof(id_ns.eui64))) {
+ jout("Namespace %u IEEE EUI-64: %s%02x%02x%02x %02x%02x%02x%02x%02x\n",
nsid, align, id_ns.eui64[0], id_ns.eui64[1], id_ns.eui64[2], id_ns.eui64[3],
id_ns.eui64[4], id_ns.eui64[5], id_ns.eui64[6], id_ns.eui64[7]);
+ jrns["eui64"]["oui"] = sg_get_unaligned_be(3, id_ns.eui64);
+ jrns["eui64"]["ext_id"] = sg_get_unaligned_be(5, id_ns.eui64 + 3);
+ }
}
- char td[DATEANDEPOCHLEN]; dateandtimezone(td);
- pout("Local Time is: %s\n", td);
+ time_t now = time(0);
+ char td[DATEANDEPOCHLEN]; dateandtimezoneepoch(td, now);
+ jout("Local Time is: %s\n", td);
+ jglb["local_time"]["time_t"] = now;
+ jglb["local_time"]["asctime"] = td;
}
// Format scaled power value.
static void print_critical_warning(unsigned char w)
{
- pout("SMART overall-health self-assessment test result: %s\n",
+ jout("SMART overall-health self-assessment test result: %s\n",
(!w ? "PASSED" : "FAILED!"));
+ jglb["smart_status"]["passed"] = !w;
+
+ json::ref jref = jglb["smart_status"]["nvme"];
+ jref["value"] = w;
if (w) {
if (w & 0x01)
- pout("- available spare has fallen below threshold\n");
+ jout("- available spare has fallen below threshold\n");
+ jref["spare_below_threshold"] = !!(w & 0x01);
if (w & 0x02)
- pout("- temperature is above or below threshold\n");
+ jout("- temperature is above or below threshold\n");
+ jref["temperature_above_or_below_threshold"] = !!(w & 0x02);
if (w & 0x04)
- pout("- NVM subsystem reliability has been degraded\n");
+ jout("- NVM subsystem reliability has been degraded\n");
+ jref["reliability_degraded"] = !!(w & 0x04);
if (w & 0x08)
- pout("- media has been placed in read only mode\n");
+ jout("- media has been placed in read only mode\n");
+ jref["media_read_only"] = !!(w & 0x08);
if (w & 0x10)
- pout("- volatile memory backup device has failed\n");
+ jout("- volatile memory backup device has failed\n");
+ jref["volatile_memory_backup_failed"] = !!(w & 0x10);
if (w & ~0x1f)
- pout("- unknown critical warning(s) (0x%02x)\n", w & ~0x1f);
+ jout("- unknown critical warning(s) (0x%02x)\n", w & ~0x1f);
+ jref["other"] = w & ~0x1f;
}
- pout("\n");
+ jout("\n");
}
-static void print_smart_log(const nvme_smart_log & smart_log, unsigned nsid,
+static void print_smart_log(const nvme_smart_log & smart_log,
const nvme_id_ctrl & id_ctrl, bool show_all)
{
+ json::ref jref = jglb["nvme_smart_health_information_log"];
char buf[64];
- pout("SMART/Health Information (NVMe Log 0x02, NSID 0x%x)\n", nsid);
- pout("Critical Warning: 0x%02x\n", smart_log.critical_warning);
- pout("Temperature: %s\n",
- kelvin_to_str(buf, le16_to_uint(smart_log.temperature)));
- pout("Available Spare: %u%%\n", smart_log.avail_spare);
- pout("Available Spare Threshold: %u%%\n", smart_log.spare_thresh);
- pout("Percentage Used: %u%%\n", smart_log.percent_used);
- pout("Data Units Read: %s\n", le128_to_str(buf, smart_log.data_units_read, 1000*512));
- pout("Data Units Written: %s\n", le128_to_str(buf, smart_log.data_units_written, 1000*512));
- pout("Host Read Commands: %s\n", le128_to_str(buf, smart_log.host_reads));
- pout("Host Write Commands: %s\n", le128_to_str(buf, smart_log.host_writes));
- pout("Controller Busy Time: %s\n", le128_to_str(buf, smart_log.ctrl_busy_time));
- pout("Power Cycles: %s\n", le128_to_str(buf, smart_log.power_cycles));
- pout("Power On Hours: %s\n", le128_to_str(buf, smart_log.power_on_hours));
- pout("Unsafe Shutdowns: %s\n", le128_to_str(buf, smart_log.unsafe_shutdowns));
- pout("Media and Data Integrity Errors: %s\n", le128_to_str(buf, smart_log.media_errors));
- pout("Error Information Log Entries: %s\n", le128_to_str(buf, smart_log.num_err_log_entries));
+ jout("SMART/Health Information (NVMe Log 0x02)\n");
+ jout("Critical Warning: 0x%02x\n", smart_log.critical_warning);
+ jref["critical_warning"] = smart_log.critical_warning;
+
+ int k = sg_get_unaligned_le16(smart_log.temperature);
+ jout("Temperature: %s\n", kelvin_to_str(buf, k));
+ if (k) {
+ jref["temperature"] = k - 273;
+ jglb["temperature"]["current"] = k - 273;
+ }
+
+ jout("Available Spare: %u%%\n", smart_log.avail_spare);
+ jref["available_spare"] = smart_log.avail_spare;
+ jout("Available Spare Threshold: %u%%\n", smart_log.spare_thresh);
+ jref["available_spare_threshold"] = smart_log.spare_thresh;
+ jout("Percentage Used: %u%%\n", smart_log.percent_used);
+ jref["percentage_used"] = smart_log.percent_used;
+ jout("Data Units Read: %s\n", le128_to_str(buf, smart_log.data_units_read, 1000*512));
+ jref["data_units_read"].set_unsafe_le128(smart_log.data_units_read);
+ jout("Data Units Written: %s\n", le128_to_str(buf, smart_log.data_units_written, 1000*512));
+ jref["data_units_written"].set_unsafe_le128(smart_log.data_units_written);
+ jout("Host Read Commands: %s\n", le128_to_str(buf, smart_log.host_reads));
+ jref["host_reads"].set_unsafe_le128(smart_log.host_reads);
+ jout("Host Write Commands: %s\n", le128_to_str(buf, smart_log.host_writes));
+ jref["host_writes"].set_unsafe_le128(smart_log.host_writes);
+ jout("Controller Busy Time: %s\n", le128_to_str(buf, smart_log.ctrl_busy_time));
+ jref["controller_busy_time"].set_unsafe_le128(smart_log.ctrl_busy_time);
+ jout("Power Cycles: %s\n", le128_to_str(buf, smart_log.power_cycles));
+ jref["power_cycles"].set_unsafe_le128(smart_log.power_cycles);
+ jglb["power_cycle_count"].set_if_safe_le128(smart_log.power_cycles);
+ jout("Power On Hours: %s\n", le128_to_str(buf, smart_log.power_on_hours));
+ jref["power_on_hours"].set_unsafe_le128(smart_log.power_on_hours);
+ jglb["power_on_time"]["hours"].set_if_safe_le128(smart_log.power_on_hours);
+ jout("Unsafe Shutdowns: %s\n", le128_to_str(buf, smart_log.unsafe_shutdowns));
+ jref["unsafe_shutdowns"].set_unsafe_le128(smart_log.unsafe_shutdowns);
+ jout("Media and Data Integrity Errors: %s\n", le128_to_str(buf, smart_log.media_errors));
+ jref["media_errors"].set_unsafe_le128(smart_log.media_errors);
+ jout("Error Information Log Entries: %s\n", le128_to_str(buf, smart_log.num_err_log_entries));
+ jref["num_err_log_entries"].set_unsafe_le128(smart_log.num_err_log_entries);
// Temperature thresholds are optional
- if (show_all || id_ctrl.wctemp || smart_log.warning_temp_time)
- pout("Warning Comp. Temperature Time: %d\n", smart_log.warning_temp_time);
- if (show_all || id_ctrl.cctemp || smart_log.critical_comp_time)
- pout("Critical Comp. Temperature Time: %d\n", smart_log.critical_comp_time);
+ if (show_all || id_ctrl.wctemp || smart_log.warning_temp_time) {
+ jout("Warning Comp. Temperature Time: %d\n", smart_log.warning_temp_time);
+ jref["warning_temp_time"] = smart_log.warning_temp_time;
+ }
+ if (show_all || id_ctrl.cctemp || smart_log.critical_comp_time) {
+ jout("Critical Comp. Temperature Time: %d\n", smart_log.critical_comp_time);
+ jref["critical_comp_time"] = smart_log.critical_comp_time;
+ }
// Temperature sensors are optional
for (int i = 0; i < 8; i++) {
- if (show_all || smart_log.temp_sensor[i])
- pout("Temperature Sensor %d: %s\n", i + 1,
- kelvin_to_str(buf, smart_log.temp_sensor[i]));
+ int k = smart_log.temp_sensor[i];
+ if (show_all || k) {
+ jout("Temperature Sensor %d: %s\n", i + 1,
+ kelvin_to_str(buf, k));
+ if (k)
+ jref["temperature_sensors"][i] = k - 273;
+ }
}
if (show_all || smart_log.thm_temp1_trans_count)
pout("Thermal Temp. 1 Transition Count: %d\n", smart_log.thm_temp1_trans_count);
// Read Identify Controller always
nvme_id_ctrl id_ctrl;
if (!nvme_read_id_ctrl(device, id_ctrl)) {
- pout("Read NVMe Identify Controller failed: %s\n", device->get_errmsg());
+ jerr("Read NVMe Identify Controller failed: %s\n", device->get_errmsg());
return FAILID;
}
else {
// Identify current namespace
if (!nvme_read_id_ns(device, nsid, id_ns)) {
- pout("Read NVMe Identify Namespace 0x%x failed: %s\n", nsid, device->get_errmsg());
+ jerr("Read NVMe Identify Namespace 0x%x failed: %s\n", nsid, device->get_errmsg());
return FAILID;
}
}
if (options.smart_check_status || options.smart_vendor_attrib) {
nvme_smart_log smart_log;
if (!nvme_read_smart_log(device, smart_log)) {
- pout("Read NVMe SMART/Health Information failed: %s\n\n", device->get_errmsg());
+ jerr("Read NVMe SMART/Health Information failed: %s\n\n", device->get_errmsg());
return FAILSMART;
}
}
if (options.smart_vendor_attrib) {
- print_smart_log(smart_log, device->get_nsid(), id_ctrl, show_all);
+ print_smart_log(smart_log, id_ctrl, show_all);
}
}
reinterpret_cast<nvme_error_log_page *>(error_log_buf.data());
if (!nvme_read_error_log(device, error_log, num_entries)) {
- pout("Read Error Information Log failed: %s\n\n", device->get_errmsg());
+ jerr("Read Error Information Log failed: %s\n\n", device->get_errmsg());
return retval | FAILSMART;
}
if (options.log_page_size) {
// Align size to dword boundary
unsigned size = ((options.log_page_size + 4-1) / 4) * 4;
+ bool broadcast_nsid;
raw_buffer log_buf(size);
- if (!nvme_read_log_page(device, options.log_page, log_buf.data(), size)) {
- pout("Read NVMe Log 0x%02x failed: %s\n\n", options.log_page, device->get_errmsg());
+ switch (options.log_page) {
+ case 1:
+ case 2:
+ case 3:
+ broadcast_nsid = true;
+ break;
+ default:
+ broadcast_nsid = false;
+ break;
+ }
+ if (!nvme_read_log_page(device, options.log_page, log_buf.data(),
+ size, broadcast_nsid)) {
+ jerr("Read NVMe Log 0x%02x failed: %s\n\n", options.log_page, device->get_errmsg());
return retval | FAILSMART;
}