X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=smartctl.cpp;h=53ee298bfb589dcc48a1b96e859c80d6f3daf5cb;hb=6c5eb127c873bd2414f28b25ba1f9bae494cf516;hp=76924af1ae6594f114e07bb17075ac8912495640;hpb=a23d51175fcd642a0e0b7874fe2f5db2b28a0a6c;p=mirror_smartmontools-debian.git diff --git a/smartctl.cpp b/smartctl.cpp index 76924af..53ee298 100644 --- a/smartctl.cpp +++ b/smartctl.cpp @@ -3,8 +3,8 @@ * * Home page of code is: http://smartmontools.sourceforge.net * - * Copyright (C) 2002-10 Bruce Allen - * Copyright (C) 2008-10 Christian Franke + * Copyright (C) 2002-11 Bruce Allen + * Copyright (C) 2008-15 Christian Franke * Copyright (C) 2000 Michael Cornwell * * This program is free software; you can redistribute it and/or modify @@ -13,8 +13,7 @@ * 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 . * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -27,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -41,42 +41,39 @@ #include #endif -#if defined(__QNXNTO__) -#include // TODO: Why is this include necessary on QNX ? -#endif - #include "int64.h" #include "atacmds.h" #include "dev_interface.h" #include "ataprint.h" -#include "extern.h" #include "knowndrives.h" #include "scsicmds.h" #include "scsiprint.h" #include "smartctl.h" #include "utility.h" -const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 3032 2010-01-16 13:04:55Z chrfranke $" - CONFIG_H_CVSID EXTERN_H_CVSID SMARTCTL_H_CVSID; +const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4080 2015-05-05 20:31:22Z chrfranke $" + CONFIG_H_CVSID SMARTCTL_H_CVSID; -// This is a block containing all the "control variables". We declare -// this globally in this file, and externally in other files. -smartmonctrl *con=NULL; +// Globals to control printing +bool printing_is_switchable = false; +bool printing_is_off = false; static void printslogan() { pout("%s\n", format_version_info("smartctl").c_str()); } -void UsageSummary(){ +static void UsageSummary() +{ pout("\nUse smartctl -h to get a usage summary\n\n"); return; } -static std::string getvalidarglist(char opt); +static std::string getvalidarglist(int opt); /* void prints help information for command syntax */ -void Usage (void){ +static void Usage() +{ printf("Usage: smartctl [options] device\n\n"); printf( "============================================ SHOW INFORMATION OPTIONS =====\n\n" @@ -84,12 +81,20 @@ void Usage (void){ " Display this help and exit\n\n" " -V, --version, --copyright, --license\n" " Print license, copyright, and version information and exit\n\n" -" -i, --info \n" +" -i, --info\n" " Show identity information for device\n\n" -" -a, --all \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, rcache, wcreorder\n\n" +" -a, --all\n" " Show all SMART information for device\n\n" " -x, --xall\n" " Show all information for device\n\n" +" --scan\n" +" Scan for devices\n\n" +" --scan-open\n" +" Scan for devices and try to open each device\n\n" ); printf( "================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n" @@ -114,6 +119,10 @@ void Usage (void){ " Enable/disable automatic offline testing on device (on/off)\n\n" " -S VALUE, --saveauto=VALUE (ATA)\n" " 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], wcreorder,[on|off]\n\n" ); printf( "======================================= READ AND DISPLAY DATA OPTIONS =====\n\n" @@ -121,33 +130,43 @@ void Usage (void){ " Show device SMART health status\n\n" " -c, --capabilities (ATA)\n" " Show device SMART capabilities\n\n" -" -A, --attributes \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: 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],\n" -" gplog,N[,RANGE], smartlog,N[,RANGE],\n" -" xerror[,N][,error], xselftest[,N][,selftest]\n\n" +" scttemp[sts,hist], scttempint,N[,p],\n" +" scterc[,N,M], devstat[,N], ssd,\n" +" gplog,N[,RANGE], smartlog,N[,RANGE]\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" -" 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 -" [default is "SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h]\n" -#endif -"\n" + printf( + "\n" +" and then %s", + get_drivedb_path_default() ); +#endif printf( + "]\n\n" "============================================ DEVICE SELF-TEST OPTIONS =====\n\n" " -t TEST, --test=TEST\n" -" Run test. TEST: offline short long conveyance select,M-N\n" -" pending,N afterselect,[on|off] scttempint,N[,p]\n\n" +" Run test. TEST: offline, short, long, conveyance, force, vendor,N,\n" +" select,M-N, pending,N, afterselect,[on|off]\n\n" " -C, --captive\n" " Do test in captive mode (along with -t)\n\n" " -X, --abort\n" @@ -158,37 +177,56 @@ void Usage (void){ printf("%s\n", examples.c_str()); } +// Values for --long only options, see parse_options() +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 */ -static std::string getvalidarglist(char opt) +static std::string getvalidarglist(int opt) { switch (opt) { case 'q': return "errorsonly, silent, noserial"; case 'd': - return smi()->get_valid_dev_types_str() + ", test"; + return smi()->get_valid_dev_types_str() + ", auto, test"; case 'T': return "normal, conservative, permissive, verypermissive"; case 'b': return "warn, exit, ignore"; case 'r': return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; - case 's': + case opt_smart: case 'o': case 'S': return "on, off"; case 'l': - return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], " - "sasphy[,reset], sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], " - "xerror[,N][,error], xselftest[,N][,selftest]"; + return "error, selftest, selective, directory[,g|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]"; + case 'P': return "use, ignore, show, showall"; case 't': - return "offline, short, long, conveyance, select,M-N, pending,N, afterselect,[on|off], scttempint,N[,p]"; + 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, hex[,id|val]"; + case 'g': + 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], 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 ""; @@ -197,8 +235,8 @@ static std::string getvalidarglist(char opt) /* Prints the message "=======> VALID ARGUMENTS ARE: \n", where is the list of valid arguments for option opt. */ -void printvalidarglistmessage(char opt) { - +static void printvalidarglistmessage(int opt) +{ if (opt=='v'){ pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", create_vendor_attribute_arg_list().c_str()); @@ -221,13 +259,16 @@ enum checksum_err_mode_t { static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN; +static void scan_devices(const char * type, bool with_open, char ** argv); + + /* Takes command options and sets features to be run */ -const char * parse_options(int argc, char** argv, - ata_print_options & ataopts, - scsi_print_options & scsiopts) +static const char * parse_options(int argc, char** argv, + ata_print_options & ataopts, scsi_print_options & scsiopts, + 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:"; + const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:"; // Please update getvalidarglist() if you edit longopts struct option longopts[] = { { "help", no_argument, 0, 'h' }, @@ -240,7 +281,7 @@ const char * parse_options(int argc, char** argv, { "tolerance", required_argument, 0, 'T' }, { "badsum", required_argument, 0, 'b' }, { "report", required_argument, 0, 'r' }, - { "smart", required_argument, 0, 's' }, + { "smart", required_argument, 0, opt_smart }, { "offlineauto", required_argument, 0, 'o' }, { "saveauto", required_argument, 0, 'S' }, { "health", no_argument, 0, 'H' }, @@ -258,59 +299,66 @@ const char * parse_options(int argc, char** argv, { "firmwarebug", required_argument, 0, 'F' }, { "nocheck", required_argument, 0, 'n' }, { "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 }, { 0, 0, 0, 0 } }; char extraerror[256]; memset(extraerror, 0, sizeof(extraerror)); - memset(con,0,sizeof(*con)); opterr=optopt=0; const char * type = 0; // set to -d optarg bool no_defaultdb = false; // set true 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; int testcnt = 0; // number of self-tests requested int optchar; char *arg; - // This miserable construction is needed to get emacs to do proper indenting. Sorry! - while (-1 != (optchar = - getopt_long(argc, argv, shortopts, longopts, NULL) - )){ + while ((optchar = getopt_long(argc, argv, shortopts, longopts, 0)) != -1) { switch (optchar){ case 'V': - con->dont_print = false; + printing_is_off = false; pout("%s", format_version_info("smartctl", true /*full*/).c_str()); EXIT(0); break; case 'q': if (!strcmp(optarg,"errorsonly")) { - con->printing_switchable = true; - con->dont_print = false; + printing_is_switchable = true; + printing_is_off = false; } else if (!strcmp(optarg,"silent")) { - con->printing_switchable = false; - con->dont_print = true; + printing_is_switchable = false; + printing_is_off = true; } else if (!strcmp(optarg,"noserial")) { - con->dont_print_serial = true; + dont_print_serial_number = true; } else { badarg = true; } break; case 'd': - type = optarg; + if (!strcmp(optarg, "test")) + print_type_only = true; + else + type = (strcmp(optarg, "auto") ? optarg : (char *)0); break; case 'T': if (!strcmp(optarg,"normal")) { - con->conservative = false; - con->permissive = 0; + failuretest_conservative = false; + failuretest_permissive = 0; } else if (!strcmp(optarg,"conservative")) { - con->conservative = true; + failuretest_conservative = true; } else if (!strcmp(optarg,"permissive")) { - if (con->permissive<0xff) - con->permissive++; + if (failuretest_permissive < 0xff) + failuretest_permissive++; } else if (!strcmp(optarg,"verypermissive")) { - con->permissive = 0xff; + failuretest_permissive = 0xff; } else { badarg = true; } @@ -339,28 +387,33 @@ const char * parse_options(int argc, char** argv, if (split_report_arg(s, &i)) { badarg = true; } else if (!strcmp(s,"ioctl")) { - con->reportataioctl = con->reportscsiioctl = i; + ata_debugmode = scsi_debugmode = i; } else if (!strcmp(s,"ataioctl")) { - con->reportataioctl = i; + ata_debugmode = i; } else if (!strcmp(s,"scsiioctl")) { - con->reportscsiioctl = i; + scsi_debugmode = i; } else { badarg = true; } free(s); } break; + case 's': + case opt_smart: // --smart if (!strcmp(optarg,"on")) { ataopts.smart_enable = scsiopts.smart_enable = true; ataopts.smart_disable = scsiopts.smart_disable = false; } else if (!strcmp(optarg,"off")) { ataopts.smart_disable = scsiopts.smart_disable = true; ataopts.smart_enable = scsiopts.smart_enable = false; + } else if (optchar == 's') { + goto case_s_continued; // --set, see below } else { badarg = true; } break; + case 'o': if (!strcmp(optarg,"on")) { ataopts.smart_auto_offl_enable = true; @@ -385,21 +438,13 @@ const char * parse_options(int argc, char** argv, break; case 'H': ataopts.smart_check_status = scsiopts.smart_check_status = true; + 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; @@ -430,6 +475,11 @@ const char * parse_options(int argc, char** argv, ataopts.sataphy = ataopts.sataphy_reset = true; } else if (!strcmp(optarg,"background")) { scsiopts.smart_background_log = true; + } else if (!strcmp(optarg,"ssd")) { + ataopts.devstat_ssd_page = true; + scsiopts.smart_ss_media_log = true; + } else if (!strcmp(optarg,"scterc")) { + ataopts.sct_erc_get = true; } else if (!strcmp(optarg,"scttemp")) { ataopts.sct_temp_sts = ataopts.sct_temp_hist = true; } else if (!strcmp(optarg,"scttempsts")) { @@ -437,6 +487,31 @@ const char * parse_options(int argc, char** argv, } else if (!strcmp(optarg,"scttemphist")) { ataopts.sct_temp_hist = true; + } else if (!strncmp(optarg, "scttempint,", sizeof("scstempint,")-1)) { + 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))) { + snprintf(extraerror, sizeof(extraerror), "Option -l scttempint,N[,p] must have positive integer N\n"); + badarg = true; + } + ataopts.sct_temp_int = interval; + ataopts.sct_temp_int_pers = (n2 == len); + + } else if (!strncmp(optarg, "devstat", sizeof("devstat")-1)) { + int n1 = -1, n2 = -1, len = strlen(optarg); + unsigned val = ~0; + sscanf(optarg, "devstat%n,%u%n", &n1, &val, &n2); + if (n1 == len) + ataopts.devstat_all_pages = 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); unsigned val = 8; @@ -467,6 +542,18 @@ const char * parse_options(int argc, char** argv, else badarg = true; + } else if (!strncmp(optarg, "scterc,", sizeof("scterc,")-1)) { + unsigned rt = ~0, wt = ~0; int n = -1; + sscanf(optarg,"scterc,%u,%u%n", &rt, &wt, &n); + if (n == (int)strlen(optarg) && rt <= 999 && wt <= 999) { + ataopts.sct_erc_set = true; + ataopts.sct_erc_readtime = rt; + ataopts.sct_erc_writetime = wt; + } + else { + snprintf(extraerror, sizeof(extraerror), "Option -l scterc,[READTIME,WRITETIME] syntax error\n"); + badarg = true; + } } else if ( !strncmp(optarg, "gplog," , sizeof("gplog," )-1) || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) { unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0; @@ -480,14 +567,14 @@ const char * parse_options(int argc, char** argv, 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 { @@ -503,6 +590,22 @@ const char * parse_options(int argc, char** argv, 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; @@ -512,6 +615,7 @@ const char * parse_options(int argc, char** argv, 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; @@ -526,14 +630,25 @@ const char * parse_options(int argc, char** argv, ataopts.smart_selective_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.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 |= ata_print_options::FMT_BRIEF; break; case 'v': // parse vendor-specific definitions of attributes if (!strcmp(optarg,"help")) { - con->dont_print = false; + printing_is_off = false; printslogan(); pout("The valid arguments to -v are:\n\thelp\n%s\n", create_vendor_attribute_arg_list().c_str()); @@ -579,6 +694,9 @@ const char * parse_options(int argc, char** argv, } else if (!strcmp(optarg,"conveyance")) { testcnt++; 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; @@ -592,28 +710,29 @@ const char * parse_options(int argc, char** argv, 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; } } else if (!strncmp(optarg,"select",strlen("select"))) { - testcnt++; + if (ataopts.smart_selective_args.num_spans == 0) + testcnt++; // 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; @@ -624,15 +743,18 @@ const char * parse_options(int argc, char** argv, ataopts.smart_selective_args.num_spans++; ataopts.smart_selftest_type = SELECTIVE_SELF_TEST; } - } else if (!strncmp(optarg, "scttempint,", sizeof("scstempint,")-1)) { - 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 -t scttempint,N[,p] must have positive integer N\n"); - badarg = true; + } else if (!strncmp(optarg, "scttempint", sizeof("scstempint")-1)) { + 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))) { + snprintf(extraerror, sizeof(extraerror), "Option -t vendor,0xNN syntax error\n"); + badarg = true; } - ataopts.sct_temp_int = interval; - ataopts.sct_temp_int_pers = (n2 == len); + else + ataopts.smart_selftest_type = subcmd; } else { badarg = true; } @@ -658,6 +780,25 @@ const char * parse_options(int argc, char** argv, else badarg = true; break; + case 'f': + 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': { const char * path = optarg; @@ -670,21 +811,147 @@ const char * parse_options(int argc, char** argv, } break; case 'h': - con->dont_print = false; + printing_is_off = false; printslogan(); Usage(); EXIT(0); break; + + case 'g': + case_s_continued: // -s, see above + case opt_set: // --set + { + ataopts.get_set_used = true; + bool get = (optchar == 'g'); + char name[16+1]; unsigned val; + 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")); + if (n3 != len) + val = ~0U; + + if (get && !strcmp(name, "all")) { + 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) + ataopts.get_aam = true; + else if (off) + ataopts.set_aam = -1; + else if (val <= 254) + ataopts.set_aam = val + 1; + else { + snprintf(extraerror, sizeof(extraerror), "Option -s aam,N must have 0 <= N <= 254\n"); + badarg = true; + } + } + else if (!strcmp(name, "apm")) { + if (get) + ataopts.get_apm = true; + else if (off) + ataopts.set_apm = -1; + else if (1 <= val && val <= 254) + ataopts.set_apm = val + 1; + else { + snprintf(extraerror, sizeof(extraerror), "Option -s apm,N must have 1 <= N <= 254\n"); + badarg = true; + } + } + else if (!strcmp(name, "lookahead")) { + if (get) { + ataopts.get_lookahead = true; + } + else if (off) + ataopts.set_lookahead = -1; + else if (on) + ataopts.set_lookahead = 1; + 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 (!get && !strcmp(optarg, "security-freeze")) { + ataopts.set_security_freeze = true; + } + else if (!get && !strcmp(optarg, "standby,now")) { + ataopts.set_standby_now = true; + } + else if (!get && !strcmp(name, "standby")) { + if (off) + ataopts.set_standby = 0 + 1; + else if (val <= 255) + ataopts.set_standby = val + 1; + else { + snprintf(extraerror, sizeof(extraerror), "Option -s standby,N must have 0 <= N <= 255\n"); + badarg = true; + } + } + else if (!strcmp(name, "wcache")) { + if (get) { + ataopts.get_wcache = true; + scsiopts.get_wce = true; + } + else if (off) { + ataopts.set_wcache = -1; + scsiopts.set_wce = -1; + } + else if (on) { + ataopts.set_wcache = 1; + scsiopts.set_wce = 1; + } + else + badarg = true; + } + else + badarg = true; + } + else + badarg = true; + } + break; + + case opt_scan: + case opt_scan_open: + scan = optchar; + break; + case '?': default: - con->dont_print = false; + printing_is_off = false; printslogan(); // Point arg to the argument in which this option was found. arg = argv[optind-1]; // Check whether the option is a long option that doesn't map to -h. if (arg[1] == '-' && optchar != 'h') { // Iff optopt holds a valid option then argument must be missing. - if (optopt && (strchr(shortopts, optopt) != NULL)) { + if (optopt && (optopt >= opt_scan || strchr(shortopts, optopt))) { pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2); printvalidarglistmessage(optopt); } else @@ -694,7 +961,7 @@ const char * parse_options(int argc, char** argv, UsageSummary(); EXIT(FAILCMD); } - if (optopt) { + if (0 < optopt && optopt < '~') { // Iff optopt holds a valid option then argument must be // missing. Note (BA) this logic seems to fail using Solaris // getopt! @@ -718,7 +985,11 @@ const char * parse_options(int argc, char** argv, // It would be nice to print the actual option name given by the user // here, but we just print the short form. Please fix this if you know // a clean way to do it. - pout("=======> INVALID ARGUMENT TO -%c: %s\n", optchar, optarg); + char optstr[] = { (char)optchar, 0 }; + pout("=======> INVALID ARGUMENT TO -%s: %s\n", + (optchar == opt_identify ? "-identify" : + optchar == opt_set ? "-set" : + optchar == opt_smart ? "-smart" : optstr), optarg); printvalidarglistmessage(optchar); if (extraerror[0]) pout("=======> %s", extraerror); @@ -726,15 +997,25 @@ const char * parse_options(int argc, char** argv, EXIT(FAILCMD); } } + + // Special handling of --scan, --scanopen + if (scan) { + // Read or init drive database to allow USB ID check. + if (!no_defaultdb && !read_default_drive_databases()) + EXIT(FAILCMD); + scan_devices(type, (scan == opt_scan_open), argv + optind); + EXIT(0); + } + // At this point we have processed all command-line options. If the // print output is switchable, then start with the print output // turned off - if (con->printing_switchable) - con->dont_print = true; + if (printing_is_switchable) + printing_is_off = true; // error message if user has asked for more than one test if (testcnt > 1) { - con->dont_print = false; + printing_is_off = false; printslogan(); pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n"); UsageSummary(); @@ -745,7 +1026,7 @@ const char * parse_options(int argc, char** argv, // asking for a selective self-test if ( (ataopts.smart_selective_args.pending_time || ataopts.smart_selective_args.scan_after_select) && !ataopts.smart_selective_args.num_spans) { - con->dont_print = false; + printing_is_off = false; printslogan(); if (ataopts.smart_selective_args.pending_time) pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n"); @@ -804,7 +1085,7 @@ const char * parse_options(int argc, char** argv, return type; } -// Printing function (controlled by global con->dont_print) +// Printing function (controlled by global printing_is_off) // [From GLIBC Manual: Since the prototype doesn't specify types for // optional arguments, in a call to a variadic function the default // argument promotions are performed on the optional argument @@ -816,7 +1097,7 @@ void pout(const char *fmt, ...){ // initialize variable argument list va_start(ap,fmt); - if (con->dont_print){ + if (printing_is_off) { va_end(ap); return; } @@ -828,19 +1109,32 @@ void pout(const char *fmt, ...){ return; } -// This function is used by utility.cpp to report LOG_CRIT errors. -// The smartctl version prints to stdout instead of syslog(). -void PrintOut(int priority, const char *fmt, ...) { - va_list ap; +// Globals to set failuretest() policy +bool failuretest_conservative = false; +unsigned char failuretest_permissive = 0; - // avoid warning message about unused variable from gcc -W: just - // change value of local copy. - priority=0; +// Compares failure type to policy in effect, and either exits or +// simply returns to the calling routine. +// Used in ataprint.cpp and scsiprint.cpp. +void failuretest(failure_type type, int returnvalue) +{ + // If this is an error in an "optional" SMART command + if (type == OPTIONAL_CMD) { + if (!failuretest_conservative) + return; + pout("An optional SMART command failed: exiting. Remove '-T conservative' option to continue.\n"); + EXIT(returnvalue); + } - va_start(ap,fmt); - vprintf(fmt,ap); - va_end(ap); - return; + // If this is an error in a "mandatory" SMART command + if (type == MANDATORY_CMD) { + if (failuretest_permissive--) + return; + pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n"); + EXIT(returnvalue); + } + + throw std::logic_error("failuretest: Unknown type"); } // Used to warn users about invalid checksums. Called from atacmds.cpp. @@ -869,27 +1163,73 @@ static const char * get_protocol_info(const smart_device * dev) } } +// Device scan +// smartctl [-d type] --scan[-open] -- [PATTERN] [smartd directive ...] +void scan_devices(const char * type, bool with_open, char ** argv) +{ + bool dont_print = !(ata_debugmode || scsi_debugmode); + + const char * pattern = 0; + int ai = 0; + if (argv[ai] && argv[ai][0] != '-') + pattern = argv[ai++]; + + smart_device_list devlist; + printing_is_off = dont_print; + bool ok = smi()->scan_smart_devices(devlist, type , pattern); + printing_is_off = false; + + if (!ok) { + pout("# scan_smart_devices: %s\n", smi()->get_errmsg()); + return; + } + + for (unsigned i = 0; i < devlist.size(); i++) { + smart_device_auto_ptr dev( devlist.release(i) ); + + if (with_open) { + printing_is_off = dont_print; + dev.replace ( dev->autodetect_open() ); + printing_is_off = false; + + if (!dev->is_open()) { + pout("# %s -d %s # %s, %s device open failed: %s\n", dev->get_dev_name(), + dev->get_dev_type(), dev->get_info_name(), + get_protocol_info(dev.get()), dev->get_errmsg()); + continue; + } + } + + pout("%s -d %s", dev->get_dev_name(), dev->get_dev_type()); + if (!argv[ai]) + pout(" # %s, %s device\n", dev->get_info_name(), get_protocol_info(dev.get())); + else { + for (int j = ai; argv[j]; j++) + pout(" %s", argv[j]); + pout("\n"); + } + + if (dev->is_open()) + dev->close(); + } +} + // Main program without exception handling -int main_worker(int argc, char **argv) +static int main_worker(int argc, char **argv) { + // Throw if runtime environment does not match compile time test. + check_config(); + // Initialize interface smart_interface::init(); if (!smi()) return 1; - // define control block for external functions - smartmonctrl control; - con=&control; - // Parse input arguments ata_print_options ataopts; scsi_print_options scsiopts; - const char * type = parse_options(argc, argv, ataopts, scsiopts); - - // '-d test' -> Report result of autodetection - bool print_type_only = (type && !strcmp(type, "test")); - if (print_type_only) - type = 0; + bool print_type_only = false; + const char * type = parse_options(argc, argv, ataopts, scsiopts, print_type_only); const char * name = argv[argc-1]; @@ -897,7 +1237,7 @@ int main_worker(int argc, char **argv) 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; } @@ -912,7 +1252,7 @@ int main_worker(int argc, char **argv) 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; }