/*
* 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-15 Christian Franke
* Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
*
* This program is free software; you can redistribute it and/or modify
* any later version.
*
* You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * (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
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
+#include <stdlib.h>
#include <stdarg.h>
#include <stdexcept>
#include <getopt.h>
#include <sys/param.h>
#endif
-#if defined(__QNXNTO__)
-#include <new> // TODO: Why is this include necessary on QNX ?
-#endif
-
#include "int64.h"
#include "atacmds.h"
#include "dev_interface.h"
#include "smartctl.h"
#include "utility.h"
-const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3517 2012-03-06 19:44:42Z chrfranke $"
+const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4162 2015-10-31 16:36:16Z chrfranke $"
CONFIG_H_CVSID SMARTCTL_H_CVSID;
// Globals to control printing
" Print license, copyright, and version information and exit\n\n"
" -i, --info\n"
" Show identity information for device\n\n"
+" --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\n\n"
+" Get device setting: all, aam, apm, lookahead, security, wcache, rcache, wcreorder\n\n"
" -a, --all\n"
" Show all SMART information for device\n\n"
" -x, --xall\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]\n\n"
+" wcache,[on|off], rcache,[on|off], wcreorder,[on|off]\n\n"
);
printf(
"======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"
" -A, --attributes\n"
" Show device SMART vendor-specific Attributes and values\n\n"
" -f FORMAT, --format=FORMAT (ATA)\n"
-" Set output format for attributes to one of: old, brief\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"
" -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"
-" Use firmware bug workaround: none, samsung, samsung2,\n"
-" samsung3, swapid\n\n"
+" Use firmware bug workaround:\n"
+" %s, swapid\n\n"
" -P TYPE, --presets=TYPE (ATA)\n"
" Drive-specific presets: use, ignore, show, showall\n\n"
" -B [+]FILE, --drivedb=[+]FILE (ATA)\n"
" Read and replace [add] drive database from FILE\n"
" [default is +%s",
+ get_valid_firmwarebug_args(),
get_drivedb_path_add()
);
#ifdef SMARTMONTOOLS_DRIVEDBDIR
}
// Values for --long only options, see parse_options()
-enum { opt_scan = 1000, opt_scan_open, opt_set, opt_smart };
+enum { opt_identify = 1000, opt_scan, opt_scan_open, opt_set, opt_smart };
/* Returns a string containing a formatted list of the valid arguments
to the option opt or empty on failure. Note 'v' case different */
return "offline, short, long, conveyance, force, vendor,N, select,M-N, "
"pending,N, afterselect,[on|off]";
case 'F':
- return "none, samsung, samsung2, samsung3, swapid";
+ return std::string(get_valid_firmwarebug_args()) + ", swapid";
case 'n':
return "never, sleep, standby, idle";
case 'f':
- return "old, brief";
+ return "old, brief, hex[,id|val]";
case 'g':
- return "aam, apm, lookahead, security, wcache";
+ return "aam, apm, lookahead, security, wcache, rcache, wcreorder";
case opt_set:
return "aam,[N|off], apm,[N|off], lookahead,[on|off], security-freeze, "
- "standby,[N|off|now], wcache,[on|off]";
+ "standby,[N|off|now], wcache,[on|off], rcache,[on|off], wcreorder,[on|off]";
case 's':
return getvalidarglist(opt_smart)+", "+getvalidarglist(opt_set);
+ case opt_identify:
+ return "n, wn, w, v, wv, wb";
case 'v':
default:
return "";
{ "drivedb", required_argument, 0, 'B' },
{ "format", required_argument, 0, 'f' },
{ "get", required_argument, 0, 'g' },
+ { "identify", optional_argument, 0, opt_identify },
{ "set", required_argument, 0, opt_set },
{ "scan", no_argument, 0, opt_scan },
{ "scan-open", no_argument, 0, opt_scan_open },
opterr=optopt=0;
const char * type = 0; // set to -d optarg
- bool no_defaultdb = false; // set true on '-B FILE'
+ 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;
scsiopts.smart_ss_media_log = true;
break;
case 'F':
- if (!strcmp(optarg,"none")) {
- ataopts.fix_firmwarebug = FIX_NONE;
- } else if (!strcmp(optarg,"samsung")) {
- ataopts.fix_firmwarebug = FIX_SAMSUNG;
- } else if (!strcmp(optarg,"samsung2")) {
- ataopts.fix_firmwarebug = FIX_SAMSUNG2;
- } else if (!strcmp(optarg,"samsung3")) {
- ataopts.fix_firmwarebug = FIX_SAMSUNG3;
- } else if (!strcmp(optarg,"swapid")) {
+ if (!strcmp(optarg, "swapid"))
ataopts.fix_swapped_id = true;
- } else {
+ else if (!parse_firmwarebug_def(optarg, ataopts.firmwarebugs))
badarg = true;
- }
break;
case 'c':
ataopts.smart_general_values = true;
unsigned interval = 0; int n1 = -1, n2 = -1, len = strlen(optarg);
if (!( sscanf(optarg,"scttempint,%u%n,p%n", &interval, &n1, &n2) == 1
&& 0 < interval && interval <= 0xffff && (n1 == len || n2 == len))) {
- strcpy(extraerror, "Option -l scttempint,N[,p] must have positive integer N\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -l scttempint,N[,p] must have positive integer N\n");
badarg = true;
}
ataopts.sct_temp_int = interval;
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
- badarg = true;
+ 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 (!strncmp(optarg, "xerror", sizeof("xerror")-1)) {
int n1 = -1, n2 = -1, len = strlen(optarg);
ataopts.sct_erc_writetime = wt;
}
else {
- sprintf(extraerror, "Option -l scterc,[READTIME,WRITETIME] syntax error\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME] syntax error\n");
badarg = true;
}
} else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1)
const char * erropt = (gpl ? "gplog" : "smartlog");
if (!( n1 == len || n2 == len
|| (n3 == len && (sign == '+' || sign == '-')))) {
- sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt);
+ snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt);
badarg = true;
}
else if (!( logaddr <= 0xff && page <= (gpl ? 0xffffU : 0x00ffU)
&& 0 < nsectors
&& (nsectors <= (gpl ? 0xffffU : 0xffU) || nsectors == ~0U)
&& (sign != '-' || page <= nsectors) )) {
- sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt);
+ snprintf(extraerror, sizeof(extraerror), "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt);
badarg = true;
}
else {
case 'i':
ataopts.drive_info = scsiopts.drive_info = true;
break;
+
+ case opt_identify:
+ ataopts.identify_word_level = ataopts.identify_bit_level = 0;
+ if (optarg) {
+ for (int i = 0; optarg[i]; i++) {
+ switch (optarg[i]) {
+ case 'w': ataopts.identify_word_level = 1; break;
+ case 'n': ataopts.identify_bit_level = -1; break;
+ case 'v': ataopts.identify_bit_level = 1; break;
+ case 'b': ataopts.identify_bit_level = 2; break;
+ default: badarg = true;
+ }
+ }
+ }
+ break;
+
case 'a':
ataopts.drive_info = scsiopts.drive_info = true;
ataopts.smart_check_status = scsiopts.smart_check_status = 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.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;
+ scsiopts.get_rcd = scsiopts.get_wce = true;
scsiopts.smart_background_log = true;
scsiopts.smart_ss_media_log = true;
scsiopts.sasphy = true;
if (!output_format_set)
- ataopts.output_format = 1; // '-f brief'
+ ataopts.output_format |= ata_print_options::FMT_BRIEF;
break;
case 'v':
// parse vendor-specific definitions of attributes
} 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));
ataopts.smart_selftest_type = CONVEYANCE_SELF_TEST;
} else if (!strcmp(optarg,"force")) {
ataopts.smart_selftest_force = true;
+ scsiopts.smart_selftest_force = true;
} else if (!strcmp(optarg,"afterselect,on")) {
// scan remainder of disk after doing selected segment
ataopts.smart_selective_args.scan_after_select = 2;
errno=0;
i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10);
if (errno || *tailptr != '\0') {
- sprintf(extraerror, "Option -t pending,N requires N to be a non-negative integer\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -t pending,N requires N to be a non-negative integer\n");
badarg = true;
} else if (i<0 || i>65535) {
- sprintf(extraerror, "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i);
+ snprintf(extraerror, sizeof(extraerror), "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i);
badarg = true;
} else {
ataopts.smart_selective_args.pending_time = i+1;
// parse range of LBAs to test
uint64_t start, stop; int mode;
if (split_selective_arg(optarg, &start, &stop, &mode)) {
- sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -t select,M-N must have non-negative integer M and N\n");
badarg = true;
} else {
if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) {
if (start > stop) {
- sprintf(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 {
- sprintf(extraerror,"ERROR: No more than five selective self-test spans may be"
+ snprintf(extraerror, sizeof(extraerror),"ERROR: No more than five selective self-test spans may be"
" defined\n");
}
badarg = true;
ataopts.smart_selftest_type = SELECTIVE_SELF_TEST;
}
} else if (!strncmp(optarg, "scttempint", sizeof("scstempint")-1)) {
- strcpy(extraerror, "-t scttempint is no longer supported, use -l scttempint instead\n");
+ snprintf(extraerror, sizeof(extraerror), "-t scttempint is no longer supported, use -l scttempint instead\n");
badarg = true;
} else if (!strncmp(optarg, "vendor,", sizeof("vendor,")-1)) {
unsigned subcmd = ~0U; int n = -1;
if (!( sscanf(optarg, "%*[a-z],0x%x%n", &subcmd, &n) == 1
&& subcmd <= 0xff && n == (int)strlen(optarg))) {
- strcpy(extraerror, "Option -t vendor,0xNN syntax error\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -t vendor,0xNN syntax error\n");
badarg = true;
}
else
badarg = true;
break;
case 'f':
- output_format_set = true;
- if (!strcmp(optarg,"old")) {
- ataopts.output_format = 0;
- } else if (!strcmp(optarg,"brief")) {
- ataopts.output_format = 1;
- } else {
- badarg = true;
+ if (!strcmp(optarg, "old")) {
+ ataopts.output_format &= ~ata_print_options::FMT_BRIEF;
+ output_format_set = true;
+ }
+ else if (!strcmp(optarg, "brief")) {
+ ataopts.output_format |= ata_print_options::FMT_BRIEF;
+ output_format_set = true;
}
+ else if (!strcmp(optarg, "hex"))
+ ataopts.output_format |= ata_print_options::FMT_HEX_ID
+ | ata_print_options::FMT_HEX_VAL;
+ else if (!strcmp(optarg, "hex,id"))
+ ataopts.output_format |= ata_print_options::FMT_HEX_ID;
+ else if (!strcmp(optarg, "hex,val"))
+ ataopts.output_format |= ata_print_options::FMT_HEX_VAL;
+ else
+ badarg = true;
break;
case 'B':
{
if (*path == '+' && path[1])
path++;
else
- no_defaultdb = true;
+ use_default_db = false;
if (!read_drive_database(path))
EXIT(FAILCMD);
}
ataopts.get_aam = ataopts.get_apm = true;
ataopts.get_security = true;
ataopts.get_lookahead = ataopts.get_wcache = true;
+ scsiopts.get_rcd = scsiopts.get_wce = true;
}
else if (!strcmp(name, "aam")) {
if (get)
else if (val <= 254)
ataopts.set_aam = val + 1;
else {
- sprintf(extraerror, "Option -s aam,N must have 0 <= N <= 254\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -s aam,N must have 0 <= N <= 254\n");
badarg = true;
}
}
else if (1 <= val && val <= 254)
ataopts.set_apm = val + 1;
else {
- sprintf(extraerror, "Option -s apm,N must have 1 <= N <= 254\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -s apm,N must have 1 <= N <= 254\n");
badarg = true;
}
}
else if (!strcmp(name, "lookahead")) {
- if (get)
+ if (get) {
ataopts.get_lookahead = true;
+ }
else if (off)
ataopts.set_lookahead = -1;
else if (on)
else
badarg = true;
}
+ else if (!strcmp(name, "wcreorder")) {
+ 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, "rcache")) {
+ if (get)
+ scsiopts.get_rcd = true;
+ else if (off)
+ scsiopts.set_rcd = -1;
+ else if (on)
+ scsiopts.set_rcd = 1;
+ else
+ badarg = true;
+ }
else if (get && !strcmp(name, "security")) {
ataopts.get_security = true;
}
else if (val <= 255)
ataopts.set_standby = val + 1;
else {
- sprintf(extraerror, "Option -s standby,N must have 0 <= N <= 255\n");
+ snprintf(extraerror, sizeof(extraerror), "Option -s standby,N must have 0 <= N <= 255\n");
badarg = true;
}
}
else if (!strcmp(name, "wcache")) {
- if (get)
+ if (get) {
ataopts.get_wcache = true;
- else if (off)
+ scsiopts.get_wce = true;
+ }
+ else if (off) {
ataopts.set_wcache = -1;
- else if (on)
+ scsiopts.set_wce = -1;
+ }
+ else if (on) {
ataopts.set_wcache = 1;
+ scsiopts.set_wce = 1;
+ }
else
badarg = true;
}
// a clean way to do it.
char optstr[] = { (char)optchar, 0 };
pout("=======> INVALID ARGUMENT TO -%s: %s\n",
- (optchar == opt_set ? "-set" :
+ (optchar == opt_identify ? "-identify" :
+ optchar == opt_set ? "-set" :
optchar == opt_smart ? "-smart" : optstr), optarg);
printvalidarglistmessage(optchar);
if (extraerror[0])
// 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);
EXIT(0);
}
// Read or init drive database
- if (!no_defaultdb && !read_default_drive_databases())
+ if (!init_drive_database(use_default_db))
EXIT(FAILCMD);
return type;
// 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();
if (!strcmp(name,"-")) {
// Parse "smartctl -r ataioctl,2 ..." output from stdin
if (type || print_type_only) {
- pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+ pout("-d option is not allowed in conjunction with device name \"-\".\n");
UsageSummary();
return FAILCMD;
}
if (type)
printvalidarglistmessage('d');
else
- pout("Smartctl: please specify device type with the -d option.\n");
+ pout("Please specify device type with the -d option.\n");
UsageSummary();
return FAILCMD;
}