]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - nvmeprint.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / nvmeprint.cpp
index fe7053e13c9ae747e66716d7eb04daaf03573662..a2cdb4b78531d3a20ee9ca15b735182ab30f85f4 100644 (file)
@@ -3,31 +3,28 @@
  *
  * 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;
 
@@ -58,9 +55,11 @@ static const char * le128_to_str(char (& str)[64], uint64_t hi, uint64_t lo, uns
     }
   }
   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;
@@ -88,6 +87,13 @@ static const char * lbacap_to_str(char (& str)[64], uint64_t lba_cnt, int lba_bi
   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)
 {
@@ -98,73 +104,96 @@ 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.
@@ -270,60 +299,101 @@ static void print_drive_capabilities(const nvme_id_ctrl & id_ctrl, const nvme_id
 
 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);
@@ -396,7 +466,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
   // 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;
   }
 
@@ -418,7 +488,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
     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;
         }
     }
@@ -439,7 +509,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
   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;
     }
 
@@ -450,7 +520,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
     }
 
     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);
     }
   }
 
@@ -462,7 +532,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
       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;
     }
 
@@ -473,10 +543,22 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
   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;
     }