/*
* smartctl.cpp
*
- * Home page of code is: http://smartmontools.sourceforge.net
+ * Home page of code is: http://www.smartmontools.org
*
- * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-11 Bruce Allen
+ * Copyright (C) 2008-17 Christian Franke
* Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
*
* This program is free software; you can redistribute it and/or modify
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
+#include <stdlib.h>
#include <stdarg.h>
#include <stdexcept>
#include <getopt.h>
#include "knowndrives.h"
#include "scsicmds.h"
#include "scsiprint.h"
+#include "nvmeprint.h"
#include "smartctl.h"
#include "utility.h"
-const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3726 2012-12-12 20:02:48Z chrfranke $"
+const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4585 2017-11-04 13:41:03Z chrfranke $"
CONFIG_H_CVSID SMARTCTL_H_CVSID;
// Globals to control printing
" --identify[=[w][nvb]]\n"
" Show words and bits from IDENTIFY DEVICE data (ATA)\n\n"
" -g NAME, --get=NAME\n"
-" Get device setting: all, aam, apm, lookahead, security, wcache, rcache\n\n"
+" Get device setting: all, aam, apm, dsn, lookahead, security,\n"
+" wcache, rcache, wcreorder, wcache-sct\n\n"
" -a, --all\n"
" Show all SMART information for device\n\n"
" -x, --xall\n"
" -q TYPE, --quietmode=TYPE (ATA)\n"
" Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n"
" -d TYPE, --device=TYPE\n"
-" Specify device type to one of: %s\n\n"
+" Specify device type to one of:\n"
+" %s\n\n" // TODO: fold this string
" -T TYPE, --tolerance=TYPE (ATA)\n"
" Tolerance: normal, conservative, permissive, verypermissive\n\n"
" -b TYPE, --badsum=TYPE (ATA)\n"
" Set action on bad checksum to one of: warn, exit, ignore\n\n"
" -r TYPE, --report=TYPE\n"
" Report transactions (see man page)\n\n"
-" -n MODE, --nocheck=MODE (ATA)\n"
+" -n MODE[,STATUS], --nocheck=MODE[,STATUS] (ATA)\n"
" No check if: never, sleep, standby, idle (see man page)\n\n",
getvalidarglist('d').c_str()); // TODO: Use this function also for other options ?
printf(
" Enable/disable Attribute autosave on device (on/off)\n\n"
" -s NAME[,VALUE], --set=NAME[,VALUE]\n"
" Enable/disable/change device setting: aam,[N|off], apm,[N|off],\n"
-" lookahead,[on|off], security-freeze, standby,[N|off|now],\n"
-" wcache,[on|off], rcache,[on|off]\n\n"
+" dsn,[on|off], lookahead,[on|off], security-freeze,\n"
+" standby,[N|off|now], wcache,[on|off], rcache,[on|off],\n"
+" wcreorder,[on|off[,p]], wcache-sct,[ata|on|off[,p]]\n\n"
);
printf(
"======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"
" -H, --health\n"
" Show device SMART health status\n\n"
-" -c, --capabilities (ATA)\n"
+" -c, --capabilities (ATA, NVMe)\n"
" Show device SMART capabilities\n\n"
" -A, --attributes\n"
" Show device SMART vendor-specific Attributes and values\n\n"
" Set output format for attributes: old, brief, hex[,id|val]\n\n"
" -l TYPE, --log=TYPE\n"
" Show device log. TYPE: error, selftest, selective, directory[,g|s],\n"
-" xerror[,N][,error], xselftest[,N][,selftest],\n"
-" background, sasphy[,reset], sataphy[,reset],\n"
-" scttemp[sts,hist], scttempint,N[,p],\n"
-" scterc[,N,M], devstat[,N], ssd,\n"
-" gplog,N[,RANGE], smartlog,N[,RANGE]\n\n"
+" xerror[,N][,error], xselftest[,N][,selftest], background,\n"
+" sasphy[,reset], sataphy[,reset], scttemp[sts,hist],\n"
+" scttempint,N[,p], scterc[,N,M], devstat[,N], defects[,N], ssd,\n"
+" gplog,N[,RANGE], smartlog,N[,RANGE], nvmelog,N,SIZE\n\n"
" -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n"
" Set display OPTION for vendor Attribute N (see man page)\n\n"
" -F TYPE, --firmwarebug=TYPE (ATA)\n"
return "normal, conservative, permissive, verypermissive";
case 'b':
return "warn, exit, ignore";
+ case 'B':
+ return "[+]<FILE_NAME>";
case 'r':
- return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
+ return "ioctl[,N], ataioctl[,N], scsiioctl[,N], nvmeioctl[,N]";
case opt_smart:
case 'o':
case 'S':
"xerror[,N][,error], xselftest[,N][,selftest], "
"background, sasphy[,reset], sataphy[,reset], "
"scttemp[sts,hist], scttempint,N[,p], "
- "scterc[,N,M], devstat[,N], ssd, "
- "gplog,N[,RANGE], smartlog,N[,RANGE]";
-
+ "scterc[,N,M], devstat[,N], defects[,N], ssd, "
+ "gplog,N[,RANGE], smartlog,N[,RANGE], "
+ "nvmelog,N,SIZE";
case 'P':
return "use, ignore, show, showall";
case 't':
case 'F':
return std::string(get_valid_firmwarebug_args()) + ", swapid";
case 'n':
- return "never, sleep, standby, idle";
+ return "never, sleep[,STATUS], standby[,STATUS], idle[,STATUS]";
case 'f':
return "old, brief, hex[,id|val]";
case 'g':
- return "aam, apm, lookahead, security, wcache, rcache";
+ return "aam, apm, dsn, lookahead, security, wcache, rcache, wcreorder, wcache-sct";
case opt_set:
- return "aam,[N|off], apm,[N|off], lookahead,[on|off], security-freeze, "
- "standby,[N|off|now], wcache,[on|off], rcache,[on|off]";
+ return "aam,[N|off], apm,[N|off], dsn,[on|off], lookahead,[on|off], security-freeze, "
+ "standby,[N|off|now], wcache,[on|off], rcache,[on|off], wcreorder,[on|off[,p]], "
+ "wcache-sct,[ata|on|off[,p]]";
case 's':
return getvalidarglist(opt_smart)+", "+getvalidarglist(opt_set);
case opt_identify:
static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN;
-static void scan_devices(const char * type, bool with_open, char ** argv);
+static void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv);
/* Takes command options and sets features to be run */
static const char * parse_options(int argc, char** argv,
ata_print_options & ataopts, scsi_print_options & scsiopts,
- bool & print_type_only)
+ nvme_print_options & nvmeopts, bool & print_type_only)
{
// Please update getvalidarglist() if you edit shortopts
const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:";
opterr=optopt=0;
const char * type = 0; // set to -d optarg
- bool no_defaultdb = false; // set true on '-B FILE'
+ smart_devtype_list scan_types; // multiple -d TYPE options for --scan
+ bool use_default_db = true; // set false on '-B FILE'
bool output_format_set = false; // set true on '-f FORMAT'
int scan = 0; // set by --scan, --scan-open
bool badarg = false, captive = false;
case 'd':
if (!strcmp(optarg, "test"))
print_type_only = true;
- else
- type = (strcmp(optarg, "auto") ? optarg : (char *)0);
+ else if (!strcmp(optarg, "auto")) {
+ type = 0;
+ scan_types.clear();
+ }
+ else {
+ type = optarg;
+ scan_types.push_back(optarg);
+ }
break;
case 'T':
if (!strcmp(optarg,"normal")) {
break;
case 'r':
{
- int i;
- char *s;
-
- // split_report_arg() may modify its first argument string, so use a
- // copy of optarg in case we want optarg for an error message.
- if (!(s = strdup(optarg))) {
- throw std::bad_alloc();
- }
- if (split_report_arg(s, &i)) {
+ int n1 = -1, n2 = -1, len = strlen(optarg);
+ char s[9+1]; unsigned i = 1;
+ sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2);
+ if (!((n1 == len || n2 == len) && 1 <= i && i <= 4)) {
badarg = true;
} else if (!strcmp(s,"ioctl")) {
- ata_debugmode = scsi_debugmode = i;
+ ata_debugmode = scsi_debugmode = nvme_debugmode = i;
} else if (!strcmp(s,"ataioctl")) {
ata_debugmode = i;
} else if (!strcmp(s,"scsiioctl")) {
scsi_debugmode = i;
+ } else if (!strcmp(s,"nvmeioctl")) {
+ nvme_debugmode = i;
} else {
badarg = true;
}
- free(s);
}
break;
}
break;
case 'H':
- ataopts.smart_check_status = scsiopts.smart_check_status = true;
+ ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true;
scsiopts.smart_ss_media_log = true;
break;
case 'F':
badarg = true;
break;
case 'c':
- ataopts.smart_general_values = true;
+ ataopts.smart_general_values = nvmeopts.drive_capabilities = true;
break;
case 'A':
- ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true;
+ ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true;
break;
case 'l':
- if (!strcmp(optarg,"error")) {
+ if (str_starts_with(optarg, "error")) {
+ int n1 = -1, n2 = -1, len = strlen(optarg);
+ unsigned val = ~0;
+ sscanf(optarg, "error%n,%u%n", &n1, &val, &n2);
ataopts.smart_error_log = scsiopts.smart_error_log = true;
+ if (n1 == len)
+ nvmeopts.error_log_entries = 16;
+ else if (n2 == len && val > 0)
+ nvmeopts.error_log_entries = val;
+ else
+ badarg = true;
} else if (!strcmp(optarg,"selftest")) {
ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true;
} else if (!strcmp(optarg, "selective")) {
sscanf(optarg, "devstat%n,%u%n", &n1, &val, &n2);
if (n1 == len)
ataopts.devstat_all_pages = true;
- else if (n2 == len && val <= 255)
- ataopts.devstat_pages.push_back(val);
+ else {
+ if (n2 != len) // retry with hex
+ sscanf(optarg, "devstat,0x%x%n", &val, &n2);
+ if (n2 == len && val <= 0xff)
+ ataopts.devstat_pages.push_back(val);
+ else
+ badarg = true;
+ }
+
+ } else if (str_starts_with(optarg, "defects")) {
+ int n1 = -1, n2 = -1, len = strlen(optarg);
+ unsigned val = ~0;
+ sscanf(optarg, "defects%n,%u%n", &n1, &val, &n2);
+ if (n1 == len)
+ ataopts.pending_defects_log = 31; // Entries of first page
+ else if (n2 == len && val <= 0xffff * 32 - 1)
+ ataopts.pending_defects_log = val;
else
badarg = true;
req.nsectors = (sign == '-' ? nsectors-page+1 : nsectors);
ataopts.log_requests.push_back(req);
}
- } else {
+ }
+
+ else if (str_starts_with(optarg, "nvmelog,")) {
+ int n = -1, len = strlen(optarg);
+ unsigned page = 0, size = 0;
+ sscanf(optarg, "nvmelog,0x%x,0x%x%n", &page, &size, &n);
+ if (n == len && page <= 0xff && 0 < size && size <= 0x4000) {
+ nvmeopts.log_page = page; nvmeopts.log_page_size = size;
+ }
+ else
+ badarg = true;
+ }
+
+ else {
badarg = true;
}
break;
case 'i':
- ataopts.drive_info = scsiopts.drive_info = true;
+ ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true;
break;
case opt_identify:
break;
case 'a':
- ataopts.drive_info = scsiopts.drive_info = true;
- ataopts.smart_check_status = scsiopts.smart_check_status = true;
- ataopts.smart_general_values = true;
- ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true;
+ ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true;
+ ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true;
+ ataopts.smart_general_values = nvmeopts.drive_capabilities = true;
+ ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true;
ataopts.smart_error_log = scsiopts.smart_error_log = true;
+ nvmeopts.error_log_entries = 16;
ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true;
ataopts.smart_selective_selftest_log = true;
/* scsiopts.smart_background_log = true; */
scsiopts.smart_ss_media_log = true;
break;
case 'x':
- ataopts.drive_info = scsiopts.drive_info = true;
- ataopts.smart_check_status = scsiopts.smart_check_status = true;
- ataopts.smart_general_values = true;
- ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true;
+ ataopts.drive_info = scsiopts.drive_info = nvmeopts.drive_info = true;
+ ataopts.smart_check_status = scsiopts.smart_check_status = nvmeopts.smart_check_status = true;
+ ataopts.smart_general_values = nvmeopts.drive_capabilities = true;
+ ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = nvmeopts.smart_vendor_attrib = true;
ataopts.smart_ext_error_log = 8;
ataopts.retry_error_log = true;
+ nvmeopts.error_log_entries = 16;
ataopts.smart_ext_selftest_log = 25;
ataopts.retry_selftest_log = true;
scsiopts.smart_error_log = scsiopts.smart_selftest_log = true;
ataopts.smart_logdir = ataopts.gp_logdir = true;
ataopts.sct_temp_sts = ataopts.sct_temp_hist = true;
ataopts.sct_erc_get = true;
+ ataopts.sct_wcache_reorder_get = true;
ataopts.devstat_all_pages = true;
+ // ataopts.pending_defects_log = 31; // TODO: Add if no longer EXPERIMENTAL
+ ataopts.pending_defects_info = true; // TODO: Remove then
ataopts.sataphy = true;
ataopts.get_set_used = true;
ataopts.get_aam = ataopts.get_apm = true;
ataopts.get_security = true;
ataopts.get_lookahead = ataopts.get_wcache = true;
+ ataopts.get_dsn = true;
scsiopts.get_rcd = scsiopts.get_wce = true;
scsiopts.smart_background_log = true;
scsiopts.smart_ss_media_log = true;
} else if (!strcmp(optarg, "show")) {
ataopts.show_presets = true;
} else if (!strcmp(optarg, "showall")) {
- if (!no_defaultdb && !read_default_drive_databases())
+ if (!init_drive_database(use_default_db))
EXIT(FAILCMD);
if (optind < argc) { // -P showall MODEL [FIRMWARE]
int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
} else {
if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) {
if (start > stop) {
- snprintf(extraerror, sizeof(extraerror), "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n",
+ snprintf(extraerror, sizeof(extraerror), "ERROR: Start LBA (%" PRIu64 ") > ending LBA (%" PRId64 ") in argument \"%s\"\n",
start, stop, optarg);
} else {
snprintf(extraerror, sizeof(extraerror),"ERROR: No more than five selective self-test spans may be"
break;
case 'n':
// skip disk check if in low-power mode
- if (!strcmp(optarg, "never"))
+ if (!strcmp(optarg, "never")) {
ataopts.powermode = 1; // do not skip, but print mode
- else if (!strcmp(optarg, "sleep"))
- ataopts.powermode = 2;
- else if (!strcmp(optarg, "standby"))
- ataopts.powermode = 3;
- else if (!strcmp(optarg, "idle"))
- ataopts.powermode = 4;
- else
- badarg = true;
+ }
+ else {
+ int n1 = -1, n2 = -1, len = strlen(optarg);
+ char s[7+1]; unsigned i = FAILPOWER;
+ sscanf(optarg, "%9[a-z]%n,%u%n", s, &n1, &i, &n2);
+ if (!((n1 == len || n2 == len) && i <= 255))
+ badarg = true;
+ else if (!strcmp(s, "sleep"))
+ ataopts.powermode = 2;
+ else if (!strcmp(s, "standby"))
+ ataopts.powermode = 3;
+ else if (!strcmp(s, "idle"))
+ ataopts.powermode = 4;
+ else
+ badarg = true;
+ ataopts.powerexit = i;
+ }
break;
case 'f':
if (!strcmp(optarg, "old")) {
if (*path == '+' && path[1])
path++;
else
- no_defaultdb = true;
+ use_default_db = false;
if (!read_drive_database(path))
EXIT(FAILCMD);
}
int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg);
if (sscanf(optarg, "%16[^,=]%n%*[,=]%n%u%n", name, &n1, &n2, &val, &n3) >= 1
&& (n1 == len || (!get && n2 > 0))) {
- bool on = (n2 > 0 && !strcmp(optarg+n2, "on"));
- bool off = (n2 > 0 && !strcmp(optarg+n2, "off"));
+ bool on = false;
+ bool off = false;
+ bool ata = false;
+ bool persistent = false;
+
+ if (n2 > 0) {
+ int len2 = strlen(optarg + n2);
+ char * tmp = strstr(optarg+n2, ",p");
+ // handle ",p" in persistent options like: wcache-sct,[ata|on|off],p
+ if (tmp && (strlen(tmp) == 2)) {
+ persistent = true;
+ len2 = strlen(optarg+n2) - 2;
+
+ // the ,p option only works for set of SCT Feature Control command
+ if (strcmp(name, "wcache-sct") != 0 &&
+ strcmp(name, "wcreorder") != 0)
+ badarg = true;
+ }
+ on = !strncmp(optarg+n2, "on", len2);
+ off = !strncmp(optarg+n2, "off", len2);
+ ata = !strncmp(optarg+n2, "ata", len2);
+ }
if (n3 != len)
val = ~0U;
ataopts.get_aam = ataopts.get_apm = true;
ataopts.get_security = true;
ataopts.get_lookahead = ataopts.get_wcache = true;
+ ataopts.get_dsn = true;
scsiopts.get_rcd = scsiopts.get_wce = true;
}
else if (!strcmp(name, "aam")) {
else
badarg = true;
}
+ else if (!strcmp(name, "wcreorder")) {
+ ataopts.sct_wcache_reorder_set_pers = persistent;
+ if (get) {
+ ataopts.sct_wcache_reorder_get = true;
+ }
+ else if (off)
+ ataopts.sct_wcache_reorder_set = -1;
+ else if (on)
+ ataopts.sct_wcache_reorder_set = 1;
+ else
+ badarg = true;
+ }
+ else if (!strcmp(name, "wcache-sct")) {
+ ataopts.sct_wcache_sct_set_pers = persistent;
+ if (get) {
+ ataopts.sct_wcache_sct_get = true;
+ }
+ else if (off)
+ ataopts.sct_wcache_sct_set = 3;
+ else if (on)
+ ataopts.sct_wcache_sct_set = 2;
+ else if (ata)
+ ataopts.sct_wcache_sct_set = 1;
+ else
+ badarg = true;
+ }
else if (!strcmp(name, "rcache")) {
if (get)
scsiopts.get_rcd = true;
else
badarg = true;
}
+ else if (!strcmp(name, "dsn")) {
+ if (get) {
+ ataopts.get_dsn = true;
+ }
+ else if (off) {
+ ataopts.set_dsn = -1;
+ }
+ else if (on) {
+ ataopts.set_dsn = 1;
+ }
+ else
+ badarg = true;
+ }
else
badarg = true;
}
// Special handling of --scan, --scanopen
if (scan) {
// Read or init drive database to allow USB ID check.
- if (!no_defaultdb && !read_default_drive_databases())
+ if (!init_drive_database(use_default_db))
EXIT(FAILCMD);
- scan_devices(type, (scan == opt_scan_open), argv + optind);
+ scan_devices(scan_types, (scan == opt_scan_open), argv + optind);
EXIT(0);
}
if (printing_is_switchable)
printing_is_off = true;
+ // Check for multiple -d TYPE options
+ if (scan_types.size() > 1) {
+ printing_is_off = false;
+ printslogan();
+ pout("ERROR: multiple -d TYPE options are only allowed with --scan\n");
+ UsageSummary();
+ EXIT(FAILCMD);
+ }
+
// error message if user has asked for more than one test
if (testcnt > 1) {
printing_is_off = false;
}
// Read or init drive database
- if (!no_defaultdb && !read_default_drive_databases())
+ if (!init_drive_database(use_default_db))
EXIT(FAILCMD);
return type;
// Return info string about device protocol
static const char * get_protocol_info(const smart_device * dev)
{
- switch ((int)dev->is_ata() | ((int)dev->is_scsi() << 1)) {
+ switch ( (int)dev->is_ata()
+ | ((int)dev->is_scsi() << 1)
+ | ((int)dev->is_nvme() << 2)) {
case 0x1: return "ATA";
case 0x2: return "SCSI";
case 0x3: return "ATA+SCSI";
+ case 0x4: return "NVMe";
default: return "Unknown";
}
}
// Device scan
// smartctl [-d type] --scan[-open] -- [PATTERN] [smartd directive ...]
-void scan_devices(const char * type, bool with_open, char ** argv)
+void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv)
{
- bool dont_print = !(ata_debugmode || scsi_debugmode);
+ bool dont_print = !(ata_debugmode || scsi_debugmode || nvme_debugmode);
const char * pattern = 0;
int ai = 0;
smart_device_list devlist;
printing_is_off = dont_print;
- bool ok = smi()->scan_smart_devices(devlist, type , pattern);
+ bool ok = smi()->scan_smart_devices(devlist, types, pattern);
printing_is_off = false;
if (!ok) {
// Main program without exception handling
static int main_worker(int argc, char **argv)
{
- // Throw if CPU endianess does not match compile time test.
- check_endianness();
+ // Throw if runtime environment does not match compile time test.
+ check_config();
// Initialize interface
smart_interface::init();
// Parse input arguments
ata_print_options ataopts;
scsi_print_options scsiopts;
+ nvme_print_options nvmeopts;
bool print_type_only = false;
- const char * type = parse_options(argc, argv, ataopts, scsiopts, print_type_only);
+ const char * type = parse_options(argc, argv, ataopts, scsiopts, nvmeopts, print_type_only);
const char * name = argv[argc-1];
pout("%s: Device of type '%s' [%s] detected\n",
dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get()));
+ if (dev->is_ata() && ataopts.powermode>=2 && dev->is_powered_down()) {
+ pout("Device is in STANDBY (OS) mode, exit(%d)\n", ataopts.powerexit);
+ return ataopts.powerexit;
+ }
+
// Open device
{
// Save old info
dev.replace( dev->autodetect_open() );
// Report if type has changed
- if ((type || print_type_only) && oldinfo.dev_type != dev->get_dev_type())
+ if ( (ata_debugmode || scsi_debugmode || nvme_debugmode || print_type_only)
+ && oldinfo.dev_type != dev->get_dev_type() )
pout("%s: Device open changed type from '%s' to '%s'\n",
dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type());
}
retval = ataPrintMain(dev->to_ata(), ataopts);
else if (dev->is_scsi())
retval = scsiPrintMain(dev->to_scsi(), scsiopts);
+ else if (dev->is_nvme())
+ retval = nvmePrintMain(dev->to_nvme(), nvmeopts);
else
// we should never fall into this branch!
- pout("%s: Neither ATA nor SCSI device\n", dev->get_info_name());
+ pout("%s: Neither ATA, SCSI nor NVMe device\n", dev->get_info_name());
dev->close();
return retval;
int main(int argc, char **argv)
{
int status;
+ bool badcode = false;
+
try {
// Do the real work ...
status = main_worker(argc, argv);
catch (const std::exception & ex) {
// Other fatal errors
printf("Smartctl: Exception: %s\n", ex.what());
+ badcode = true;
status = FAILCMD;
}
+
+ // Check for remaining device objects
+ if (smart_device::get_num_objects() != 0) {
+ printf("Smartctl: Internal Error: %d device object(s) left at exit.\n",
+ smart_device::get_num_objects());
+ badcode = true;
+ status = FAILCMD;
+ }
+
+ if (badcode)
+ printf("Please inform " PACKAGE_BUGREPORT ", including output of smartctl -V.\n");
+
return status;
}