diff --git a/smartmontools/atacmds.cpp b/smartmontools/atacmds.cpp index 87810090e3cd42fa540aa3d8d8e4284074eb94ba..9e96ecff0c046c21e8e9c52769388a13a1a86cf5 100644 --- a/smartmontools/atacmds.cpp +++ b/smartmontools/atacmds.cpp @@ -1479,10 +1479,9 @@ int ataDisableAutoSave(ata_device * device){ // marked "OBSOLETE". It is defined in SFF-8035i Revision 2, and most // vendors still support it for backwards compatibility. IBM documents // it for some drives. -int ataEnableAutoOffline (ata_device * device){ +int ataEnableAutoOffline (ata_device * device, int timeout){ - /* timer hard coded to 4 hours */ - if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){ + if (smartcommandhandler(device, AUTO_OFFLINE, timeout, NULL)){ syserror("Error SMART Enable Automatic Offline failed"); return -1; } diff --git a/smartmontools/atacmds.h b/smartmontools/atacmds.h index f5b8becaacb27be686830c2e477e809e942e9de3..a4cdd6968205b61eef854ae776e8815529efadfb 100644 --- a/smartmontools/atacmds.h +++ b/smartmontools/atacmds.h @@ -735,7 +735,7 @@ int ataEnableAutoSave(ata_device * device); int ataDisableAutoSave(ata_device * device); /* Automatic Offline Testing */ -int ataEnableAutoOffline (ata_device * device); +int ataEnableAutoOffline (ata_device * device, int timeout); int ataDisableAutoOffline (ata_device * device); /* S.M.A.R.T. test commands */ diff --git a/smartmontools/ataprint.cpp b/smartmontools/ataprint.cpp index 51fef8ac8957197365c45eac778bd88b25f5acf0..7b2015a07876e7f3fc44b3080c6c45c92ed91469 100644 --- a/smartmontools/ataprint.cpp +++ b/smartmontools/ataprint.cpp @@ -1929,12 +1929,44 @@ int ataPrintMain (ata_device * device, const ata_print_options & options) failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } needupdate=1; - if (ataEnableAutoOffline(device)){ + + int atat = secs_to_atatimer(options.smart_auto_offl_timeout); + + if (ataEnableAutoOffline(device, atat)){ pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } - else - pout("SMART Automatic Offline Testing Enabled every four hours.\n"); + else { + pout("SMART Automatic Offline Testing Enabled"); + + int timeout; + + timeout = atatimer_to_secs(atat); + + if (timeout > 0) { + + pout(" every"); + + if (timeout==8*3600) { + pout(" 8-12 hours"); + } else { + int hours, mins; + + hours = timeout / 3600; + timeout%=3600; + mins = timeout / 60; + timeout%=60; + + if (hours > 0) + pout(" %d hour%s", hours, hours>1?"s":""); + if (mins > 0) + pout(" %d minute%s", mins, mins>1?"s":""); + if (timeout > 0) + pout(" %d second%s", timeout, timeout>1?"s":""); + }; + }; + pout(".\n"); + } } if (options.smart_auto_offl_disable) { diff --git a/smartmontools/ataprint.h b/smartmontools/ataprint.h index 6c8c5eef27bbd88143076ccd5ebf331e74015ea5..56828459955fd3ae26309a52139ea25bf5b4f223 100644 --- a/smartmontools/ataprint.h +++ b/smartmontools/ataprint.h @@ -67,6 +67,7 @@ struct ata_print_options bool smart_disable, smart_enable; bool smart_auto_offl_disable, smart_auto_offl_enable; + int smart_auto_offl_timeout; bool smart_auto_save_disable, smart_auto_save_enable; int smart_selftest_type; // OFFLINE_FULL_SCAN, ..., see atacmds.h. -1 for no test @@ -100,6 +101,7 @@ struct ata_print_options sataphy(false), sataphy_reset(false), smart_disable(false), smart_enable(false), smart_auto_offl_disable(false), smart_auto_offl_enable(false), + smart_auto_offl_timeout(4*3600), smart_auto_save_disable(false), smart_auto_save_enable(false), smart_selftest_type(-1), sct_temp_int(0), sct_temp_int_pers(false), diff --git a/smartmontools/smartctl.cpp b/smartmontools/smartctl.cpp index 93ba92894bcdb981938510528d4fb0674fcd7b46..f5a12c7ab61afbf657b4fdbf585ed48c5e7afa4e 100644 --- a/smartmontools/smartctl.cpp +++ b/smartmontools/smartctl.cpp @@ -362,9 +362,47 @@ const char * parse_options(int argc, char** argv, } break; case 'o': - if (!strcmp(optarg,"on")) { + if (!strncmp(optarg,"on", 2)) { ataopts.smart_auto_offl_enable = true; ataopts.smart_auto_offl_disable = false; + if (optarg[2]=='\0') { + ataopts.smart_auto_offl_timeout = 4*3600; /* default is 4 hours */ + break; + }; + if (optarg[2]!=',' || optarg[3]=='\0') { + badarg = true; + break; + }; + + long timeout; + char *endptr; + + timeout = strtol(optarg+3, &endptr, 0); + if (timeout < 1) { + badarg = true; // non-positive number + break; + }; + + switch (*endptr) { + case 's': + endptr++; + break; + case 'm': + timeout*=60; + endptr++; + break; + case 'h': + timeout*=3600; + endptr++; + break; + }; + + if (*endptr != '\0') { + badarg = true; // extra character after parameter + break; + }; + + ataopts.smart_auto_offl_timeout = (int)timeout; } else if (!strcmp(optarg,"off")) { ataopts.smart_auto_offl_disable = true; ataopts.smart_auto_offl_enable = false; diff --git a/smartmontools/smartd.cpp b/smartmontools/smartd.cpp index 85044e7f5dec82c2596c0acce90c9c5711741594..663254b5e6569dd2afe1ced9bbb0c96e2bdea906 100644 --- a/smartmontools/smartd.cpp +++ b/smartmontools/smartd.cpp @@ -258,6 +258,7 @@ struct dev_config bool permissive; // Ignore failed SMART commands char autosave; // 1=disable, 2=enable Autosave Attributes char autoofflinetest; // 1=disable, 2=enable Auto Offline Test + int autoofflinetestperiod; // seconds between two Auto Offline Test unsigned char fix_firmwarebug; // FIX_*, see atacmds.h bool ignorepresets; // Ignore database of -v options bool showpresets; // Show database entry for this device @@ -299,6 +300,7 @@ dev_config::dev_config() permissive(false), autosave(0), autoofflinetest(0), + autoofflinetestperiod(4*3600), fix_firmwarebug(FIX_NOTSPECIFIED), ignorepresets(false), showpresets(false), @@ -1748,7 +1750,7 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade if (!isSupportAutomaticTimer(&state.smartval)) PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name); // ... but then try anyway - if ((cfg.autoofflinetest==1)?ataDisableAutoOffline(atadev):ataEnableAutoOffline(atadev)) + if ((cfg.autoofflinetest==1)?ataDisableAutoOffline(atadev):ataEnableAutoOffline(atadev, secs_to_atatimer(cfg.autoofflinetestperiod))) PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what); else PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what); @@ -3202,8 +3204,46 @@ static int ParseToken(char * token, dev_config & cfg) // automatic offline testing enable/disable if ((arg = strtok(NULL, delim)) == NULL) { missingarg = 1; - } else if (!strcmp(arg, "on")) { + } else if (!strncmp(arg, "on", 2)) { cfg.autoofflinetest = 2; + if (arg[2]=='\0') { + cfg.autoofflinetestperiod = 4*3600; /* default value */ + break; + }; + if (optarg[2]!=',' || optarg[3]=='\0') { + badarg = 1; + break; + }; + + long timeout; + char *endptr; + + timeout = strtol(optarg+3, &endptr, 0); + if (timeout < 1) { + badarg = 1; // non-positive number + break; + }; + + switch (*endptr) { + case 's': + endptr++; + break; + case 'm': + timeout*=60; + endptr++; + break; + case 'h': + timeout*=3600; + endptr++; + break; + }; + + if (*endptr != '\0') { + badarg = 1; // extra character after parameter + break; + }; + + cfg.autoofflinetestperiod = (int)timeout; } else if (!strcmp(arg, "off")) { cfg.autoofflinetest = 1; } else { diff --git a/smartmontools/utility.cpp b/smartmontools/utility.cpp index 134bf3349ad853bdcb70aa2da7c09e1ae6074b8d..c9cbe6c04a2e9edbffcc98423308e675adf2a855 100644 --- a/smartmontools/utility.cpp +++ b/smartmontools/utility.cpp @@ -778,3 +778,62 @@ int safe_snprintf(char *buf, int size, const char *fmt, ...) } #endif + +// Convert seconds to ata timer scale and vice versa +// +// +---------------------+------------------------------+ +// | Sector Count | Corresponding | +// | Register contents | Timeout Period | +// +---------------------+------------------------------+ +// | 0 (00h) | Timeout Disabled | +// | 1 - 240 (01h-F0h) | (value * 5) seconds | +// | 241 - 251 (F1h-FBh) | ((value - 240) * 30) minutes | +// | 252 (FCh) | 21 minutes | +// | 253 (FDh) | Vendor unique period between | +// | | 8 and 12 hours | +// | 254 (FEh) | Reserved | +// | 255 (FFh) | 21 minutes 15 seconds | +// +---------------------+------------------------------+ +// (Table taken from X3T10/0948D Revision 4c) +unsigned int +secs_to_atatimer(unsigned int tmo) { + + if (tmo == 0) + return 0; + if (tmo < 5) + return 1; + + if (tmo<=1200) + return tmo / 5; + if (tmo<1200+60) + return 240; + if (tmo<1200+75) + return 252; + if (tmo<1800) + return 255; + if (tmo <19800+1800 ) + return (tmo / 1800)+240; + + if (tmo < 8*3600) + return 251; + + return 253; +}; + +unsigned int +atatimer_to_secs(unsigned int atat) { + + switch (atat) { + case 0: return 0; + case 252: return 21*60; + case 253: return 8*3600; + case 254: return -1; + case 255: return 21*60+15; + }; + + if (atat<=240) + return(atat*5); + + return((atat-240)*1800); + +}; diff --git a/smartmontools/utility.h b/smartmontools/utility.h index 215e9d2aa0c35e87ced0f947f359f2adf7cceb66..b62faf1faa8dab30f1d55eb15db3599222457a58 100644 --- a/smartmontools/utility.h +++ b/smartmontools/utility.h @@ -335,3 +335,7 @@ private: #endif #endif + +unsigned int secs_to_atatimer(unsigned int tmo); +unsigned int atatimer_to_secs(unsigned int atat); +