diff --git a/smartmontools/CHANGELOG b/smartmontools/CHANGELOG index 81f67a8701cb28514bb6a4915f42f68dba489d1e..825abeea5f3bd6857d741ae4b798c5e008c01825 100644 --- a/smartmontools/CHANGELOG +++ b/smartmontools/CHANGELOG @@ -43,13 +43,17 @@ NOTES FOR FUTURE RELEASES: see TODO file. <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE> + [CF] Rework ATA SMART attribute check in smartctl and smartd. + smartd: Ignore normalized attribute value and threshold + if 'raw64' or 'hex64' format is selected. + [CF] Add USB IDs of Iomega LPHD080-0, 2 Genesys Logic bridges and Initio 316000. [MS] knowndrives.cpp update: Hitachi Travelstar 5K320 series [CF] smartctl: Ignore normalized attribute value and threshold - if 'raw64' or 'hex24' format is selected. + if 'raw64' or 'hex64' format is selected. [CF] knowndrives.cpp updates: - add OCZ-Vertex raw64 attributes diff --git a/smartmontools/atacmds.cpp b/smartmontools/atacmds.cpp index ff2e2282a360fbc4a5dab569601fd0a889b742d9..c218cf0a21537ddc4ff1c5214c6bcfea39207126 100644 --- a/smartmontools/atacmds.cpp +++ b/smartmontools/atacmds.cpp @@ -1703,37 +1703,45 @@ int isSupportSelectiveSelfTest(const ata_smart_values * data) return data->offline_data_collection_capability & 0x40; } -// This checks the n'th attribute in the attribute list, NOT the -// attribute with id==n. If the attribute does not exist, or the -// attribute is > threshold, then returns zero. If the attribute is -// <= threshold (failing) then we the attribute number if it is a -// prefail attribute. Else we return minus the attribute number if it -// is a usage attribute. -int ataCheckAttribute(const ata_smart_values * data, - const ata_smart_thresholds_pvt * thresholds, - int n) -{ - if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds) - return 0; - - // pointers to disk's values and vendor's thresholds - const ata_smart_attribute * disk = data->vendor_attributes+n; - const ata_smart_threshold_entry * thre = thresholds->thres_entries+n; +// Get attribute state +ata_attr_state ata_get_attr_state(const ata_smart_attribute & attr, + const ata_smart_threshold_entry & thre, + const ata_vendor_attr_defs & defs) +{ + if (!attr.id) + return ATTRSTATE_NON_EXISTING; - if (!disk || !thre) - return 0; - - // consider only valid attributes, check for failure - if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold) - return 0; - - // We have found a failed attribute. Return positive or negative? - if (ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)) - return disk->id; - else - return -1*(disk->id); -} + // Normalized values (current,worst,threshold) not valid + // if specified by '-v' option. + // (Some SSD disks uses these bytes to store raw value). + if (defs[attr.id].flags & ATTRFLAG_NO_NORMVAL) + return ATTRSTATE_NO_NORMVAL; + + // No threshold if thresholds cannot be read. + if (!thre.id && !thre.threshold) + return ATTRSTATE_NO_THRESHOLD; + + // Bad threshold if id's don't match + if (attr.id != thre.id) + return ATTRSTATE_BAD_THRESHOLD; + + // Don't report a failed attribute if its threshold is 0. + // ATA-3 (X3T13/2008D Revision 7b) declares 0x00 as the "always passing" + // threshold (Later ATA versions declare all thresholds as "obsolete"). + // In practice, threshold value 0 is often used for usage attributes. + if (!thre.threshold) + return ATTRSTATE_OK; + // Failed now if current value is below threshold + if (attr.current <= thre.threshold) + return ATTRSTATE_FAILED_NOW; + + // Failed in the passed if worst value is below threshold + if (attr.worst <= thre.threshold) + return ATTRSTATE_FAILED_PAST; + + return ATTRSTATE_OK; +} // Get default raw value print format static ata_attr_raw_format get_default_raw_format(unsigned char id) @@ -1755,18 +1763,13 @@ static ata_attr_raw_format get_default_raw_format(unsigned char id) } } -// Format attribute raw value. -std::string ata_format_attr_raw_value(const ata_smart_attribute & attribute, - const ata_vendor_attr_defs & defs) +// Get attribute raw value. +uint64_t ata_get_attr_raw_value(const ata_smart_attribute & attr, + const ata_vendor_attr_defs & defs) { - // Get print format - ata_attr_raw_format format = defs[attribute.id].raw_format; - if (format == RAWFMT_DEFAULT) - format = get_default_raw_format(attribute.id); - // Get 48 bit raw value - const unsigned char * raw = attribute.raw; - int64_t rawvalue; + const unsigned char * raw = attr.raw; + uint64_t rawvalue; rawvalue = raw[0] | ( raw[1] << 8) | ( raw[2] << 16) @@ -1774,12 +1777,37 @@ std::string ata_format_attr_raw_value(const ata_smart_attribute & attribute, | ((uint64_t)raw[4] << 32) | ((uint64_t)raw[5] << 40); + if (defs[attr.id].flags & ATTRFLAG_NO_NORMVAL) { + // Some SSD vendors use bytes 3-10 from the Attribute + // Data Structure to store a 64-bit raw value. + rawvalue <<= 8; + rawvalue |= attr.worst; + rawvalue <<= 8; + rawvalue |= attr.current; + } + return rawvalue; +} + + +// Format attribute raw value. +std::string ata_format_attr_raw_value(const ata_smart_attribute & attr, + const ata_vendor_attr_defs & defs) +{ + // Get 48 bit or64 bit raw value + uint64_t rawvalue = ata_get_attr_raw_value(attr, defs); + // Get 16 bit words + const unsigned char * raw = attr.raw; unsigned word[3]; word[0] = raw[0] | (raw[1] << 8); word[1] = raw[2] | (raw[3] << 8); word[2] = raw[4] | (raw[5] << 8); + // Get print format + ata_attr_raw_format format = defs[attr.id].raw_format; + if (format == RAWFMT_DEFAULT) + format = get_default_raw_format(attr.id); + // Print std::string s; switch (format) { @@ -1801,18 +1829,11 @@ std::string ata_format_attr_raw_value(const ata_smart_attribute & attribute, break; case RAWFMT_RAW64: + s = strprintf("%"PRIu64, rawvalue); + break; + case RAWFMT_HEX64: - // Some SSD vendors use bytes 3-10 from the Attribute - // Data Structure to store a 64-bit raw value. - // TODO: Do not print VALUE/WORST/THRESH in attribute table. - rawvalue <<= 8; - rawvalue |= attribute.worst; - rawvalue <<= 8; - rawvalue |= attribute.current; - if (format == RAWFMT_RAW64) - s = strprintf("%"PRIu64, rawvalue); - else - s = strprintf("0x%016"PRIx64, rawvalue); + s = strprintf("0x%016"PRIx64, rawvalue); break; case RAWFMT_RAW16_OPT_RAW16: @@ -2068,38 +2089,15 @@ std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs return get_default_attr_name(id); } -// Returns raw value of Attribute with ID==id. This will be in the -// range 0 to 2^48-1 inclusive. If the Attribute does not exist, -// return -1. -int64_t ATAReturnAttributeRawValue(unsigned char id, const ata_smart_values * data) +// Find attribute index for attribute id, -1 if not found. +int ata_find_attr_index(unsigned char id, const ata_smart_values & smartval) { - // valid Attribute IDs are in the range 1 to 255 inclusive. - if (!id || !data) + if (!id) return -1; - - // loop over Attributes to see if there is one with the desired ID for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { - const ata_smart_attribute * ap = data->vendor_attributes + i; - if (ap->id == id) { - // we've found the desired Attribute. Return its value - int64_t rawvalue=0; - int j; - - for (j=0; j<6; j++) { - // This looks a bit roundabout, but is necessary. Don't - // succumb to the temptation to use raw[j]<<(8*j) since under - // the normal rules this will be promoted to the native type. - // On a 32 bit machine this might then overflow. - int64_t temp; - temp = ap->raw[j]; - temp <<= 8*j; - rawvalue |= temp; - } // loop over j - return rawvalue; - } // found desired Attribute - } // loop over Attributes - - // fall-through: no such Attribute found + if (smartval.vendor_attributes[i].id == id) + return i; + } return -1; } @@ -2114,9 +2112,10 @@ unsigned char ata_return_temperature_value(const ata_smart_values * data, const if (!( (id == 194 && format == RAWFMT_DEFAULT) || format == RAWFMT_TEMPMINMAX || format == RAWFMT_TEMP10X)) continue; - int64_t raw = ATAReturnAttributeRawValue(id, data); - if (raw < 0) + int idx = ata_find_attr_index(id, *data); + if (idx < 0) continue; + uint64_t raw = ata_get_attr_raw_value(data->vendor_attributes[idx], defs); unsigned temp = (unsigned short)raw; // ignore possible min/max values in high words if (format == RAWFMT_TEMP10X) // -v N,temp10x temp = (temp+5) / 10; diff --git a/smartmontools/atacmds.h b/smartmontools/atacmds.h index 2fea4126ca2eb045a2b1b0637735d315f7058ce3..8d5279d35990aa0bfbec86f8c11dcf4dcfb65c9a 100644 --- a/smartmontools/atacmds.h +++ b/smartmontools/atacmds.h @@ -798,34 +798,43 @@ int ataSmartTest(ata_device * device, int testtype, const ata_selective_selftest int TestTime(const ata_smart_values * data, int testtype); +// Attribute state +enum ata_attr_state +{ + ATTRSTATE_NON_EXISTING, // No such Attribute + ATTRSTATE_NO_NORMVAL, // Normalized value not valid + ATTRSTATE_BAD_THRESHOLD, // Threshold not valid + ATTRSTATE_NO_THRESHOLD, // Unknown or no threshold + ATTRSTATE_OK, // Never failed + ATTRSTATE_FAILED_PAST, // Failed in the past + ATTRSTATE_FAILED_NOW // Failed now +}; + +// Get attribute state +ata_attr_state ata_get_attr_state(const ata_smart_attribute & attr, + const ata_smart_threshold_entry & thre, + const ata_vendor_attr_defs & defs); + +// Get attribute raw value. +uint64_t ata_get_attr_raw_value(const ata_smart_attribute & attr, + const ata_vendor_attr_defs & defs); + // Format attribute raw value. -std::string ata_format_attr_raw_value(const ata_smart_attribute & attribute, +std::string ata_format_attr_raw_value(const ata_smart_attribute & attr, const ata_vendor_attr_defs & defs); // Get attribute name std::string ata_get_smart_attr_name(unsigned char id, const ata_vendor_attr_defs & defs); -// This checks the n'th attribute in the attribute list, NOT the -// attribute with id==n. If the attribute does not exist, or the -// attribute is > threshold, then returns zero. If the attribute is -// <= threshold (failing) then we the attribute number if it is a -// prefail attribute. Else we return minus the attribute number if it -// is a usage attribute. -int ataCheckAttribute(const ata_smart_values * data, - const ata_smart_thresholds_pvt * thresholds, - int n); - // External handler function, for when a checksum is not correct. Can // simply return if no action is desired, or can print error messages // as needed, or exit. Is passed a string with the name of the Data // Structure with the incorrect checksum. void checksumwarning(const char *string); -// Returns raw value of Attribute with ID==id. This will be in the -// range 0 to 2^48-1 inclusive. If the Attribute does not exist, -// return -1. -int64_t ATAReturnAttributeRawValue(unsigned char id, const ata_smart_values * data); +// Find attribute index for attribute id, -1 if not found. +int ata_find_attr_index(unsigned char id, const ata_smart_values & smartval); // Return Temperature Attribute raw value selected according to possible // non-default interpretations. If the Attribute does not exist, return 0 diff --git a/smartmontools/ataprint.cpp b/smartmontools/ataprint.cpp index 9935206c074dfaac87fa6e73b7ce8318ab61c583..258c11f102a40012efe823b7e21869394b83a18b 100644 --- a/smartmontools/ataprint.cpp +++ b/smartmontools/ataprint.cpp @@ -781,22 +781,19 @@ static int find_failed_attr(const ata_smart_values * data, const ata_vendor_attr_defs & defs, int onlyfailed) { for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { - const ata_smart_attribute * disk = data->vendor_attributes+i; - const ata_smart_threshold_entry * thre = thresholds->thres_entries+i; + const ata_smart_attribute & attr = data->vendor_attributes[i]; - // consider only valid attributes - if (!(disk->id && disk->id == thre->id && thre->threshold - && !(defs[disk->id].flags & ATTRFLAG_NO_NORMVAL) )) - continue; - - bool failednow = (disk->current <= thre->threshold); - bool failedever = (disk->worst <= thre->threshold); + ata_attr_state state = ata_get_attr_state(attr, + thresholds->thres_entries[i], defs); - if (!onlyfailed && failedever) - return disk->id; - - if (onlyfailed && failednow && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)) - return disk->id; + if (!onlyfailed) { + if (state >= ATTRSTATE_FAILED_PAST) + return attr.id; + } + else { + if (state == ATTRSTATE_FAILED_NOW && ATTRIBUTE_FLAGS_PREFAILURE(attr.flags)) + return attr.id; + } } return 0; } @@ -809,83 +806,64 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data, const ata_vendor_attr_defs & defs, int onlyfailed) { - int needheader=1; + bool needheader = true; // step through all vendor attributes for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { - const ata_smart_attribute * disk = data->vendor_attributes+i; - const ata_smart_threshold_entry * thre = thresholds->thres_entries+i; - - // consider only valid attributes (allowing some screw-ups in the - // thresholds page data to slip by) - if (disk->id){ - // Normalized values (current,worth,threshold) valid ? - // (Some SSD disks uses these bytes to store raw value). - bool norm_values_ok = !(defs[disk->id].flags & ATTRFLAG_NO_NORMVAL); - - // Don't report a failed attribute if its threshold is 0. - // ATA-3 (X3T13/2008D Revision 7b) declares 0x00 as the "always passing" - // threshold (Later ATA versions declare all thresholds as "obsolete"). - // In practice, threshold value 0 is often used for usage attributes or - // appears if the thresholds cannot be read. - bool failednow, failedever; - if (norm_values_ok && thre->threshold) { - failednow = (disk->current <= thre->threshold); - failedever = (disk->worst <= thre->threshold); - } - else - failednow = failedever = false; + const ata_smart_attribute & attr = data->vendor_attributes[i]; + const ata_smart_threshold_entry & thre = thresholds->thres_entries[i]; - // These break out of the loop if we are only printing certain entries... - if (onlyfailed==1 && (!ATTRIBUTE_FLAGS_PREFAILURE(disk->flags) || !failednow)) - continue; - - if (onlyfailed==2 && !failedever) - continue; - - // print header only if needed - if (needheader){ - if (!onlyfailed){ - pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber); - pout("Vendor Specific SMART Attributes with Thresholds:\n"); - } - pout("ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE\n"); - needheader=0; - } - - // is this Attribute currently failed, or has it ever failed? - const char * status; - if (failednow) - status="FAILING_NOW"; - else if (failedever) - status="In_the_past"; - else - status=" -"; - - // printing line for each valid attribute - std::string attrname = ata_get_smart_attr_name(disk->id, defs); - std::string rawstr = ata_format_attr_raw_value(*disk, defs); - const char * type = ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age"; - const char * update = ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline"; - - if (norm_values_ok) - pout("%3d %-24s0x%04x %.3d %.3d %.3d %-10s%-9s%-12s%s\n", - disk->id, attrname.c_str(), disk->flags, - disk->current, disk->worst, thre->threshold, - type, update, status, rawstr.c_str()); - else - pout("%3d %-24s0x%04x --- --- --- %-10s%-9s%-12s%s\n", - disk->id, attrname.c_str(), disk->flags, - type, update, status, rawstr.c_str()); - - // Print a warning if there is inconsistency here and - // threshold info is not empty. - if (norm_values_ok && disk->id != thre->id && (thre->id || thre->threshold)) { - pout("%3d %-24s<== Data Page | WARNING: PREVIOUS ATTRIBUTE HAS TWO\n", - disk->id, attrname.c_str()); - pout("%3d %-24s<== Threshold Page | INCONSISTENT IDENTITIES IN THE DATA\n", - thre->id, attrname.c_str()); + // Check attribute and threshold + ata_attr_state state = ata_get_attr_state(attr, thre, defs); + if (state == ATTRSTATE_NON_EXISTING) + continue; + + // These break out of the loop if we are only printing certain entries... + if (onlyfailed == 1 && !(ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) && state == ATTRSTATE_FAILED_NOW)) + continue; + + if (onlyfailed == 2 && state < ATTRSTATE_FAILED_PAST) + continue; + + // print header only if needed + if (needheader) { + if (!onlyfailed) { + pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber); + pout("Vendor Specific SMART Attributes with Thresholds:\n"); } + pout("ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE\n"); + needheader = false; + } + + // Format value, worst, threshold + std::string valstr, threstr; + if (state > ATTRSTATE_NO_NORMVAL) + valstr = strprintf("%.3d %.3d", attr.current, attr.worst); + else + valstr = "--- ---"; + if (state > ATTRSTATE_NO_THRESHOLD) + threstr = strprintf("%.3d", thre.threshold); + else + threstr = "---"; + + // Print line for each valid attribute + std::string attrname = ata_get_smart_attr_name(attr.id, defs); + pout("%3d %-24s0x%04x %-9s %-3s %-10s%-9s%-12s%s\n", + attr.id, attrname.c_str(), attr.flags, + valstr.c_str(), threstr.c_str(), + (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags)? "Pre-fail" : "Old_age"), + (ATTRIBUTE_FLAGS_ONLINE(attr.flags)? "Always" : "Offline"), + (state == ATTRSTATE_FAILED_NOW ? "FAILING_NOW" : + state == ATTRSTATE_FAILED_PAST ? "In_the_past" : + " -" ), + ata_format_attr_raw_value(attr, defs).c_str()); + + // Print a warning if there is inconsistency here + if (state == ATTRSTATE_BAD_THRESHOLD) { + pout("%3d %-24s<== Data Page | WARNING: PREVIOUS ATTRIBUTE HAS TWO\n", + attr.id, attrname.c_str()); + pout("%3d %-24s<== Threshold Page | INCONSISTENT IDENTITIES IN THE DATA\n", + thre.id, ata_get_smart_attr_name(thre.id, defs).c_str()); } } if (!needheader) pout("\n"); diff --git a/smartmontools/smartd.cpp b/smartmontools/smartd.cpp index 18d23085fae28e894575648f8fa0902a5c3080f0..c8f2bca622bb97d2c839b4ad64cbdaaaad735490 100644 --- a/smartmontools/smartd.cpp +++ b/smartmontools/smartd.cpp @@ -1710,13 +1710,13 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade // see if the necessary Attribute is there to monitor offline or // current pending sectors or temperature - if (cfg.curr_pending_id && ATAReturnAttributeRawValue(cfg.curr_pending_id, &state.smartval) < 0) { + if (cfg.curr_pending_id && ata_find_attr_index(cfg.curr_pending_id, state.smartval) < 0) { PrintOut(LOG_INFO,"Device: %s, can't monitor Current Pending Sector count - no Attribute %d\n", name, cfg.curr_pending_id); cfg.curr_pending_id = 0; } - if (cfg.offl_pending_id && ATAReturnAttributeRawValue(cfg.offl_pending_id, &state.smartval) < 0) { + if (cfg.offl_pending_id && ata_find_attr_index(cfg.offl_pending_id, state.smartval) < 0) { PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count - no Attribute %d\n", name, cfg.offl_pending_id); cfg.offl_pending_id = 0; @@ -1997,75 +1997,6 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs return 0; } - -struct changedattribute_t { - unsigned char newval; - unsigned char oldval; - unsigned char id; - unsigned char prefail; - unsigned char sameraw; -}; - -// We compare old and new values of the n'th attribute. Note that n -// is NOT the attribute ID number.. If (Normalized & Raw) equal, -// then return 0, else nonzero. -static int ATACompareValues(changedattribute_t *delta, - struct ata_smart_values *newv, - struct ata_smart_values *oldv, - struct ata_smart_thresholds_pvt *thresholds, - int n, const char * name) -{ - struct ata_smart_attribute *now,*was; - struct ata_smart_threshold_entry *thre; - unsigned char oldval,newval; - int sameraw; - - // check that attribute number in range, and no null pointers - if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !newv || !oldv || !thresholds) - return 0; - - // pointers to disk's values and vendor's thresholds - now=newv->vendor_attributes+n; - was=oldv->vendor_attributes+n; - thre=thresholds->thres_entries+n; - - // consider only valid attributes - if (!now->id || !was->id || !thre->id) - return 0; - - - // issue warning if they don't have the same ID in all structures: - if ( (now->id != was->id) || (now->id != thre->id) ){ - PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n", - name, (int)now->id, (int)was->id, (int)thre->id); - return 0; - } - - // new and old values of Normalized Attributes - newval=now->current; - oldval=was->current; - - // See if the RAW values are unchanged (ie, the same) - if (memcmp(now->raw, was->raw, 6)) - sameraw=0; - else - sameraw=1; - - // if any values out of the allowed range, or if the values haven't - // changed, return 0 - if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw)) - return 0; - - // values have changed. Construct output and return - delta->newval=newval; - delta->oldval=oldval; - delta->id=now->id; - delta->prefail=ATTRIBUTE_FLAGS_PREFAILURE(now->flags); - delta->sameraw=sameraw; - - return 1; -} - // If the self-test log has got more self-test errors (or more recent // self-test errors) recorded, then notify user. static void CheckSelfTestLogs(const dev_config & cfg, dev_state & state, int newi) @@ -2434,13 +2365,18 @@ static void check_pending(const dev_config & cfg, dev_state & state, const ata_smart_values & smartval, int mailtype, const char * msg) { + // Find attribute index + int i = ata_find_attr_index(id, smartval); + if (!(i >= 0 && ata_find_attr_index(id, state.smartval) == i)) + return; + // No report if no sectors pending. - int64_t rawval = ATAReturnAttributeRawValue(id, &smartval); - if (rawval <= 0) + uint64_t rawval = ata_get_attr_raw_value(smartval.vendor_attributes[i], cfg.attribute_defs); + if (rawval == 0) return; // If attribute is not reset, report only sector count increases. - int64_t prev_rawval = ATAReturnAttributeRawValue(id, &state.smartval); + uint64_t prev_rawval = ata_get_attr_raw_value(state.smartval.vendor_attributes[i], cfg.attribute_defs); if (!(!increase_only || prev_rawval < rawval)) return; @@ -2533,6 +2469,103 @@ static void CheckTemperature(const dev_config & cfg, dev_state & state, unsigned } } +// Check normalized and raw attribute values. +static void check_attribute(const dev_config & cfg, dev_state & state, + const ata_smart_attribute & attr, + const ata_smart_attribute & prev, + const ata_smart_threshold_entry & thre) +{ + // Check attribute and threshold + ata_attr_state attrstate = ata_get_attr_state(attr, thre, cfg.attribute_defs); + if (attrstate == ATTRSTATE_NON_EXISTING) + return; + + // If requested, check for usage attributes that have failed. + if ( cfg.usagefailed && attrstate == ATTRSTATE_FAILED_NOW + && !cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGN_FAILUSE)) { + std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs); + PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", cfg.name.c_str(), attr.id, attrname.c_str()); + MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", cfg.name.c_str(), attr.id, attrname.c_str()); + state.must_write = true; + } + + // Return if we're not tracking this type of attribute + bool prefail = !!ATTRIBUTE_FLAGS_PREFAILURE(attr.flags); + if (!( ( prefail && cfg.prefail) + || (!prefail && cfg.usage ))) + return; + + // Return if '-I ID' was specified + if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGNORE)) + return; + + // Issue warning if they don't have the same ID in all structures. + if (attr.id != prev.id || attrstate == ATTRSTATE_BAD_THRESHOLD) { + PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n", + cfg.name.c_str(), attr.id, prev.id, thre.id); + return; + } + + // Compare normalized values if valid. + bool valchanged = false; + if (attrstate > ATTRSTATE_NO_NORMVAL) { + if (attr.current != prev.current) + valchanged = true; + } + + // Compare raw values if requested. + bool rawchanged = false; + if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW)) { + if ( ata_get_attr_raw_value(attr, cfg.attribute_defs) + != ata_get_attr_raw_value(prev, cfg.attribute_defs)) + rawchanged = true; + } + + // Return if no change + if (!(valchanged || rawchanged)) + return; + + // Format value strings + std::string currstr, prevstr; + if (attrstate == ATTRSTATE_NO_NORMVAL) { + // Print raw values only + currstr = strprintf("%s (Raw)", + ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str()); + prevstr = strprintf("%s (Raw)", + ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str()); + } + else if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_PRINT)) { + // Print normalized and raw values + currstr = strprintf("%d [Raw %s]", attr.current, + ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str()); + prevstr = strprintf("%d [Raw %s]", prev.current, + ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str()); + } + else { + // Print normalized values only + currstr = strprintf("%d", attr.current); + prevstr = strprintf("%d", prev.current); + } + + // Format message + std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %s to %s", + cfg.name.c_str(), (prefail ? "Prefailure" : "Usage"), attr.id, + ata_get_smart_attr_name(attr.id, cfg.attribute_defs).c_str(), + prevstr.c_str(), currstr.c_str()); + + // Report this change as critical ? + if ( (valchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_AS_CRIT)) + || (rawchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_AS_CRIT))) { + PrintOut(LOG_CRIT, "%s\n", msg.c_str()); + MailWarning(cfg, state, 2, "%s", msg.c_str()); + } + else { + PrintOut(LOG_INFO, "%s\n", msg.c_str()); + } + state.must_write = true; +} + + static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device * atadev, bool allow_selftests) { const char * name = cfg.name.c_str(); @@ -2643,10 +2676,9 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device if ( cfg.usagefailed || cfg.prefail || cfg.usage || cfg.curr_pending_id || cfg.offl_pending_id || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit || cfg.selftest) { - struct ata_smart_values curval; - struct ata_smart_thresholds_pvt * thresh = &state.smartthres; - // Read current attribute values. *drive contains old values and thresholds + // Read current attribute values. + ata_smart_values curval; if (ataReadSmartValues(atadev, &curval)){ PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name); MailWarning(cfg, state, 6, "Device: %s, failed to read SMART Attribute Data", name); @@ -2670,79 +2702,14 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device if (cfg.usagefailed || cfg.prefail || cfg.usage) { - // look for failed usage attributes, or track usage or prefail attributes + // look for failed usage attributes, or track usage or prefail attributes for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { + check_attribute(cfg, state, + curval.vendor_attributes[i], + state.smartval.vendor_attributes[i], + state.smartthres.thres_entries[i]); + } - // This block looks for usage attributes that have failed. - // Prefail attributes that have failed are returned with a - // positive sign. No failure returns 0. Usage attributes<0. - int att; - if (cfg.usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){ - - // are we ignoring failures of this attribute? - att *= -1; - if (!cfg.monitor_attr_flags.is_set(att, MONITOR_IGN_FAILUSE)) { - // warning message - std::string attrname = ata_get_smart_attr_name(att, cfg.attribute_defs); - PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", name, att, attrname.c_str()); - MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", name, att, attrname.c_str()); - state.must_write = true; - } - } - - // This block tracks usage or prefailure attributes to see if - // they are changing. It also looks for changes in RAW values - // if this has been requested by user. - changedattribute_t delta; - if ((cfg.usage || cfg.prefail) && ATACompareValues(&delta, &curval, &state.smartval, thresh, i, name)){ - - // Continue if we're not tracking this type of attribute - if (!( ( delta.prefail && cfg.prefail) - || (!delta.prefail && cfg.usage ))) - continue; - - // Continue if '-I ID' was specified - unsigned char id = delta.id; - if (cfg.monitor_attr_flags.is_set(id, MONITOR_IGNORE)) - continue; - - // if the only change is the raw value, and we're not - // tracking raw value, then continue loop over attributes - if ( !delta.sameraw && delta.newval == delta.oldval - && !cfg.monitor_attr_flags.is_set(id, MONITOR_RAW)) - continue; - - // get attribute name - std::string attrname = ata_get_smart_attr_name(id, cfg.attribute_defs); - - // has the user asked for us to print raw values? - std::string newraw, oldraw; - if (cfg.monitor_attr_flags.is_set(id, MONITOR_RAW_PRINT)) { - // get raw values (as a string) and add to printout - newraw = strprintf(" [Raw %s]", - ata_format_attr_raw_value(curval.vendor_attributes[i], cfg.attribute_defs).c_str()); - oldraw = strprintf(" [Raw %s]", - ata_format_attr_raw_value(state.smartval.vendor_attributes[i], cfg.attribute_defs).c_str()); - } - - // Format message - std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %d%s to %d%s", - name, (delta.prefail ? "Prefailure" : "Usage"), id, attrname.c_str(), - delta.oldval, oldraw.c_str(), delta.newval, newraw.c_str()); - - // Report this change as critical ? - if ( (delta.newval != delta.oldval && cfg.monitor_attr_flags.is_set(id, MONITOR_AS_CRIT)) - || (!delta.sameraw && cfg.monitor_attr_flags.is_set(id, MONITOR_RAW_AS_CRIT))) { - PrintOut(LOG_CRIT, "%s\n", msg.c_str()); - MailWarning(cfg, state, 2, "%s", msg.c_str()); - } - else { - PrintOut(LOG_INFO, "%s\n", msg.c_str()); - } - state.must_write = true; - } // endof block tracking usage or prefailure - } // end of loop over attributes - if (cfg.selftest) { // Log changes of self-test execution status if ( curval.self_test_exec_status != state.smartval.self_test_exec_status