/* * scsiprint.cpp * * Home page of code is: http://www.smartmontools.org * * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2000 Michael Cornwell * Copyright (C) 2003-18 Douglas Gilbert * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++ #include #include #include #include #include #include "scsicmds.h" #include "atacmds.h" // smart_command_set #include "dev_interface.h" #include "scsiprint.h" #include "smartctl.h" #include "utility.h" #include "sg_unaligned.h" #define GBUF_SIZE 65532 const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4870 2018-12-27 17:07:44Z chrfranke $" SCSIPRINT_H_CVSID; uint8_t gBuf[GBUF_SIZE]; #define LOG_RESP_LEN 252 #define LOG_RESP_LONG_LEN ((62 * 256) + 252) #define LOG_RESP_TAPE_ALERT_LEN 0x144 /* Log pages supported */ static bool gSmartLPage = false; /* Informational Exceptions log page */ static bool gTempLPage = false; static bool gSelfTestLPage = false; static bool gStartStopLPage = false; static bool gReadECounterLPage = false; static bool gWriteECounterLPage = false; static bool gVerifyECounterLPage = false; static bool gNonMediumELPage = false; static bool gLastNErrorEvLPage = false; static bool gBackgroundResultsLPage = false; static bool gProtocolSpecificLPage = false; static bool gTapeAlertsLPage = false; static bool gSSMediaLPage = false; static bool gFormatStatusLPage = false; static bool gEnviroReportingLPage = false; static bool gEnviroLimitsLPage = false; static bool gUtilizationLPage = false; static bool gPendDefectsLPage = false; static bool gBackgroundOpLPage = false; static bool gLPSMisalignLPage = false; /* Vendor specific log pages */ static bool gSeagateCacheLPage = false; static bool gSeagateFactoryLPage = false; /* Mode pages supported */ static bool gIecMPage = true; /* N.B. assume it until we know otherwise */ /* Remember last successful mode sense/select command */ static int modese_len = 0; /* Remember this value from the most recent INQUIRY */ static int scsi_version; #define SCSI_VERSION_SPC_4 0x6 #define SCSI_VERSION_SPC_5 0x7 #define SCSI_VERSION_HIGHEST SCSI_VERSION_SPC_5 /* T10 vendor identification. Should match entry in last Annex of SPC * drafts and standards (e.g. SPC-4). */ static char scsi_vendor[8+1]; #define T10_VENDOR_SEAGATE "SEAGATE" #define T10_VENDOR_HITACHI_1 "HITACHI" #define T10_VENDOR_HITACHI_2 "HL-DT-ST" #define T10_VENDOR_HITACHI_3 "HGST" static const char * logSenStr = "Log Sense"; static const char * logSenRspStr = "Log Sense response"; static bool seagate_or_hitachi(void) { return ((0 == memcmp(scsi_vendor, T10_VENDOR_SEAGATE, strlen(T10_VENDOR_SEAGATE))) || (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_1, strlen(T10_VENDOR_HITACHI_1))) || (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_2, strlen(T10_VENDOR_HITACHI_2))) || (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_3, strlen(T10_VENDOR_HITACHI_3)))); } static bool all_ffs(const uint8_t * bp, int b_len) { if ((NULL == bp) || (b_len <= 0)) return false; for (--b_len; b_len >= 0; --b_len) { if (0xff != bp[b_len]) return false; } return true; } static void scsiGetSupportedLogPages(scsi_device * device) { bool got_subpages = false; int k, bump, err, payload_len, num_unreported, num_unreported_spg; const uint8_t * up; uint8_t sup_lpgs[LOG_RESP_LEN]; memset(gBuf, 0, LOG_RESP_LEN); if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, LOG_RESP_LEN, 0))) { if (scsi_debugmode > 0) pout("%s for supported pages failed [%s]\n", logSenStr, scsiErrString(err)); /* try one more time with defined length, workaround for the bug #678 found with ST8000NM0075/E001 */ err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, LOG_RESP_LEN, 68); /* 64 max pages + 4b header */ if (scsi_debugmode > 0) pout("%s for supported pages failed (second attempt) [%s]\n", logSenStr, scsiErrString(err)); if (err) return; memcpy(sup_lpgs, gBuf, LOG_RESP_LEN); } else if ((scsi_version >= SCSI_VERSION_SPC_4) && (scsi_version <= SCSI_VERSION_HIGHEST)) { /* unclear what code T10 will choose for SPC-6 */ memcpy(sup_lpgs, gBuf, LOG_RESP_LEN); if ((err = scsiLogSense(device, SUPPORTED_LPAGES, SUPP_SPAGE_L_SPAGE, gBuf, LOG_RESP_LONG_LEN, -1 /* just single not double fetch */))) { if (scsi_debugmode > 0) pout("%s for supported pages and subpages failed [%s]\n", logSenStr, scsiErrString(err)); } else { if (0 == memcmp(gBuf, sup_lpgs, LOG_RESP_LEN)) { if (scsi_debugmode > 0) pout("%s: %s ignored subpage field, bad\n", __func__, logSenRspStr); } else if (! ((0x40 & gBuf[0]) && (SUPP_SPAGE_L_SPAGE == gBuf[1]))) { if (scsi_debugmode > 0) pout("%s supported subpages is bad SPF=%u SUBPG=%u\n", logSenRspStr, !! (0x40 & gBuf[0]), gBuf[2]); } else got_subpages = true; } } else memcpy(sup_lpgs, gBuf, LOG_RESP_LEN); if (got_subpages) { payload_len = sg_get_unaligned_be16(gBuf + 2); bump = 2; up = gBuf + LOGPAGEHDRSIZE; } else { payload_len = sup_lpgs[3]; bump = 1; up = sup_lpgs + LOGPAGEHDRSIZE; } num_unreported_spg = 0; for (num_unreported = 0, k = 0; k < payload_len; k += bump, up += bump) { uint8_t pg_num = 0x3f & up[0]; uint8_t sub_pg_num = (0x40 & up[0]) ? up[1] : 0; switch (pg_num) { case SUPPORTED_LPAGES: if (! ((NO_SUBPAGE_L_SPAGE == sub_pg_num) || (SUPP_SPAGE_L_SPAGE == sub_pg_num))) { if (scsi_debugmode > 1) pout("%s: Strange Log page number: 0x0,0x%x\n", __func__, sub_pg_num); } break; case READ_ERROR_COUNTER_LPAGE: gReadECounterLPage = true; break; case WRITE_ERROR_COUNTER_LPAGE: gWriteECounterLPage = true; break; case VERIFY_ERROR_COUNTER_LPAGE: gVerifyECounterLPage = true; break; case LAST_N_ERROR_EVENTS_LPAGE: gLastNErrorEvLPage = true; break; case NON_MEDIUM_ERROR_LPAGE: gNonMediumELPage = true; break; case TEMPERATURE_LPAGE: if (NO_SUBPAGE_L_SPAGE == sub_pg_num) gTempLPage = true; else if (ENVIRO_REP_L_SPAGE == sub_pg_num) gEnviroReportingLPage = true; else if (ENVIRO_LIMITS_L_SPAGE == sub_pg_num) gEnviroLimitsLPage = true; else { ++num_unreported; ++num_unreported_spg; } break; case STARTSTOP_CYCLE_COUNTER_LPAGE: if (NO_SUBPAGE_L_SPAGE == sub_pg_num) gStartStopLPage = true; else if (UTILIZATION_L_SPAGE == sub_pg_num) gUtilizationLPage = true; else { ++num_unreported; ++num_unreported_spg; } break; case SELFTEST_RESULTS_LPAGE: gSelfTestLPage = true; break; case IE_LPAGE: gSmartLPage = true; break; case BACKGROUND_RESULTS_LPAGE: if (NO_SUBPAGE_L_SPAGE == sub_pg_num) gBackgroundResultsLPage = true; else if (PEND_DEFECTS_L_SPAGE == sub_pg_num) gPendDefectsLPage = true; else if (BACKGROUND_OP_L_SPAGE == sub_pg_num) gBackgroundOpLPage = true; else if (LPS_MISALIGN_L_SPAGE == sub_pg_num) gLPSMisalignLPage = true; else { ++num_unreported; ++num_unreported_spg; } break; case PROTOCOL_SPECIFIC_LPAGE: gProtocolSpecificLPage = true; break; case TAPE_ALERTS_LPAGE: gTapeAlertsLPage = true; break; case SS_MEDIA_LPAGE: gSSMediaLPage = true; break; case FORMAT_STATUS_LPAGE: gFormatStatusLPage = true; break; case SEAGATE_CACHE_LPAGE: if (failuretest_permissive) { gSeagateCacheLPage = true; break; } if (seagate_or_hitachi()) gSeagateCacheLPage = true; break; case SEAGATE_FACTORY_LPAGE: if (failuretest_permissive) { gSeagateFactoryLPage = true; break; } if (seagate_or_hitachi()) gSeagateFactoryLPage = true; break; default: if (pg_num < 0x30) { /* don't count VS pages */ ++num_unreported; if (sub_pg_num > 0) ++num_unreported_spg; } break; } } if (scsi_debugmode > 1) pout("%s: number of unreported (standard) log pages: %d (sub-pages: " "%d)\n", __func__, num_unreported, num_unreported_spg); } /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad (or at least something to report). */ static int scsiGetSmartData(scsi_device * device, bool attribs) { uint8_t asc; uint8_t ascq; uint8_t currenttemp = 255; uint8_t triptemp = 255; const char * cp; int err = 0; print_on(); if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq, ¤ttemp, &triptemp)) { /* error message already announced */ print_off(); return -1; } print_off(); cp = scsiGetIEString(asc, ascq); if (cp) { err = -2; print_on(); jout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); print_off(); jglb["smart_status"]["passed"] = false; jglb["smart_status"]["scsi"]["asc"] = asc; jglb["smart_status"]["scsi"]["ascq"] = ascq; jglb["smart_status"]["scsi"]["ie_string"] = cp; } else if (gIecMPage) { jout("SMART Health Status: OK\n"); jglb["smart_status"]["passed"] = true; } if (attribs && !gTempLPage) { if (255 == currenttemp) pout("Current Drive Temperature: \n"); else { jout("Current Drive Temperature: %d C\n", currenttemp); jglb["temperature"]["current"] = currenttemp; } if (255 == triptemp) pout("Drive Trip Temperature: \n"); else { jout("Drive Trip Temperature: %d C\n", triptemp); jglb["temperature"]["drive_trip"] = triptemp; } } pout("\n"); return err; } // Returns number of logged errors or zero if none or -1 if fetching // TapeAlerts fails static const char * const severities = "CWI"; static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) { unsigned short pagelength; unsigned short parametercode; int i, err; const char *s; const char *ts; int failures = 0; print_on(); if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf, LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) { pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return -1; } if (gBuf[0] != 0x2e) { pout("TapeAlerts %s Failed\n", logSenStr); print_off(); return -1; } pagelength = sg_get_unaligned_be16(gBuf + 2); for (s=severities; *s; s++) { for (i = 4; i < pagelength; i += 5) { parametercode = sg_get_unaligned_be16(gBuf + i); if (gBuf[i + 4]) { ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ? scsiTapeAlertsChangerDevice(parametercode) : scsiTapeAlertsTapeDevice(parametercode); if (*ts == *s) { if (!failures) pout("TapeAlert Errors (C=Critical, W=Warning, " "I=Informational):\n"); pout("[0x%02x] %s\n", parametercode, ts); failures += 1; } } } } print_off(); if (! failures) pout("TapeAlert: OK\n"); return failures; } static void scsiGetStartStopData(scsi_device * device) { int err, len, k, extra; unsigned char * ucp; if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return; } if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) { print_on(); pout("StartStop %s Failed, page mismatch\n", logSenStr); print_off(); return; } len = sg_get_unaligned_be16(gBuf + 2); ucp = gBuf + 4; for (k = len; k > 0; k -= extra, ucp += extra) { if (k < 3) { print_on(); pout("StartStop %s: short\n", logSenRspStr); print_off(); return; } extra = ucp[3] + 4; int pc = sg_get_unaligned_be16(ucp + 0); uint32_t u = (extra > 7) ? sg_get_unaligned_be32(ucp + 4) : 0; bool is_all_ffs = (extra > 7) ? all_ffs(ucp + 4, 4) : false; switch (pc) { case 1: if (10 == extra) pout("Manufactured in week %.2s of year %.4s\n", ucp + 8, ucp + 4); break; case 2: /* ignore Accounting date */ break; case 3: if ((extra > 7) && (! is_all_ffs)) pout("Specified cycle count over device lifetime: %u\n", u); break; case 4: if ((extra > 7) && (! is_all_ffs)) pout("Accumulated start-stop cycles: %u\n", u); break; case 5: if ((extra > 7) && (! is_all_ffs)) pout("Specified load-unload count over device lifetime: " "%u\n", u); break; case 6: if ((extra > 7) && (! is_all_ffs)) pout("Accumulated load-unload cycles: %u\n", u); break; default: /* ignore */ break; } } } /* PENDING_DEFECTS_SUBPG [0x15,0x1] introduced: SBC-4 */ static void scsiPrintPendingDefectsLPage(scsi_device * device) { int num, pl, pc, err; uint32_t count; const uint8_t * bp; static const char * pDefStr = "Pending Defects"; static const char * jname = "pending_defects"; if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, PEND_DEFECTS_L_SPAGE, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return; } if (((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) && (gBuf[1] != PEND_DEFECTS_L_SPAGE)) { print_on(); pout("%s %s, page mismatch\n", pDefStr, logSenRspStr); print_off(); return; } num = sg_get_unaligned_be16(gBuf + 2); if (num > LOG_RESP_LONG_LEN) { print_on(); pout("%s %s too long\n", pDefStr, logSenRspStr); print_off(); return; } bp = gBuf + 4; while (num > 3) { pc = sg_get_unaligned_be16(bp + 0); pl = bp[3] + 4; switch (pc) { case 0x0: printf(" Pending defect count:"); if ((pl < 8) || (num < 8)) { print_on(); pout("%s truncated descriptor\n", pDefStr); print_off(); return; } count = sg_get_unaligned_be32(bp + 4); jglb[jname]["count"] = count; if (0 == count) jout("0 %s\n", pDefStr); else if (1 == count) jout("1 Pending Defect, LBA and accumulated_power_on_hours " "follow\n"); else jout("%u %s: index, LBA and accumulated_power_on_hours " "follow\n", count, pDefStr); break; default: if ((pl < 16) || (num < 16)) { print_on(); pout("%s truncated descriptor\n", pDefStr); print_off(); return; } jout(" %4d: 0x%-16" PRIx64 ", %5u\n", pc, sg_get_unaligned_be64(bp + 8), sg_get_unaligned_be32(bp + 4)); jglb[jname][pc]["LBA"] = sg_get_unaligned_be64(bp + 8); jglb[jname][pc]["accum_power_on_hours"] = sg_get_unaligned_be32(bp + 4); break; } num -= pl; bp += pl; } } static void scsiPrintGrownDefectListLen(scsi_device * device) { bool got_rd12; int err, dl_format; unsigned int dl_len, div; static const char * hname = "Read defect list"; memset(gBuf, 0, 8); if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */, 4 /* format: bytes from index */, 0 /* addr desc index */, gBuf, 8))) { if (2 == err) { /* command not supported */ err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, 4 /* format: bytes from index */, gBuf, 4); if (err) { if (scsi_debugmode > 0) { print_on(); pout("%s (10) Failed: %s\n", hname, scsiErrString(err)); print_off(); } return; } else got_rd12 = 0; } else if (101 == err) /* Defect list not found, leave quietly */ return; else { if (scsi_debugmode > 0) { print_on(); pout("%s (12) Failed: %s\n", hname, scsiErrString(err)); print_off(); } return; } } else got_rd12 = true; if (got_rd12) { int generation = sg_get_unaligned_be16(gBuf + 2); if ((generation > 1) && (scsi_debugmode > 0)) { print_on(); pout("%s (12): generation=%d\n", hname, generation); print_off(); } dl_len = sg_get_unaligned_be32(gBuf + 4); } else dl_len = sg_get_unaligned_be16(gBuf + 2); if (0x8 != (gBuf[1] & 0x18)) { print_on(); pout("%s: asked for grown list but didn't get it\n", hname); print_off(); return; } div = 0; dl_format = (gBuf[1] & 0x7); switch (dl_format) { case 0: /* short block */ div = 4; break; case 1: /* extended bytes from index */ case 2: /* extended physical sector */ /* extended = 1; # might use in future */ div = 8; break; case 3: /* long block */ case 4: /* bytes from index */ case 5: /* physical sector */ div = 8; break; default: print_on(); pout("defect list format %d unknown\n", dl_format); print_off(); break; } if (0 == dl_len) { jout("Elements in grown defect list: 0\n\n"); jglb["scsi_grown_defect_list"] = 0; } else { if (0 == div) pout("Grown defect list length=%u bytes [unknown " "number of elements]\n\n", dl_len); else { jout("Elements in grown defect list: %u\n\n", dl_len / div); jglb["scsi_grown_defect_list"] = dl_len; } } } static void scsiPrintSeagateCacheLPage(scsi_device * device) { int num, pl, pc, err, len; unsigned char * ucp; uint64_t ull; static const char * seaCacStr = "Seagate Cache"; if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { if (scsi_debugmode > 0) { print_on(); pout("%s %s Failed: %s\n", seaCacStr, logSenStr, scsiErrString(err)); print_off(); } return; } if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) { if (scsi_debugmode > 0) { print_on(); pout("%s %s, page mismatch\n", seaCacStr, logSenRspStr); print_off(); } return; } len = sg_get_unaligned_be16(gBuf + 2) + 4; num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); pl = ucp[3] + 4; switch (pc) { case 0: case 1: case 2: case 3: case 4: break; default: if (scsi_debugmode > 0) { print_on(); pout("Vendor (%s) lpage has unexpected parameter, skip\n", seaCacStr); print_off(); } return; } num -= pl; ucp += pl; } pout("Vendor (%s) information\n", seaCacStr); num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); pl = ucp[3] + 4; switch (pc) { case 0: pout(" Blocks sent to initiator"); break; case 1: pout(" Blocks received from initiator"); break; case 2: pout(" Blocks read from cache and sent to initiator"); break; case 3: pout(" Number of read and write commands whose size " "<= segment size"); break; case 4: pout(" Number of read and write commands whose size " "> segment size"); break; default: pout(" Unknown Seagate parameter code [0x%x]", pc); break; } int k = pl - 4; const int sz_ull = (int)sizeof(ull); unsigned char * xp = ucp + 4; if (k > sz_ull) { xp += (k - sz_ull); k = sz_ull; } ull = sg_get_unaligned_be(k, xp + 0); pout(" = %" PRIu64 "\n", ull); num -= pl; ucp += pl; } pout("\n"); } static void scsiPrintSeagateFactoryLPage(scsi_device * device) { int num, pl, pc, len, err, good, bad; unsigned char * ucp; uint64_t ull; if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { if (scsi_debugmode > 0) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); } return; } if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) { if (scsi_debugmode > 0) { print_on(); pout("Seagate/Hitachi Factory %s, page mismatch\n", logSenRspStr); print_off(); } return; } len = sg_get_unaligned_be16(gBuf + 2) + 4; num = len - 4; ucp = &gBuf[0] + 4; good = 0; bad = 0; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); pl = ucp[3] + 4; switch (pc) { case 0: case 8: ++good; break; default: ++bad; break; } num -= pl; ucp += pl; } if ((good < 2) || (bad > 4)) { /* heuristic */ if (scsi_debugmode > 0) { print_on(); pout("\nVendor (Seagate/Hitachi) factory lpage has too many " "unexpected parameters, skip\n"); print_off(); } return; } pout("Vendor (Seagate/Hitachi) factory information\n"); num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = sg_get_unaligned_be16(ucp + 0); pl = ucp[3] + 4; good = 0; switch (pc) { case 0: pout(" number of hours powered up"); good = 1; break; case 8: pout(" number of minutes until next internal SMART test"); good = 1; break; default: if (scsi_debugmode > 0) { print_on(); pout("Vendor (Seagate/Hitachi) factory lpage: " "unknown parameter code [0x%x]\n", pc); print_off(); } break; } if (good) { int k = pl - 4; unsigned char * xp = ucp + 4; if (k > (int)sizeof(ull)) { xp += (k - (int)sizeof(ull)); k = (int)sizeof(ull); } ull = sg_get_unaligned_be(k, xp + 0); if (0 == pc) { pout(" = %.2f\n", ull / 60.0 ); jglb["power_on_time"]["hours"] = ull / 60; jglb["power_on_time"]["minutes"] = ull % 60; } else pout(" = %" PRIu64 "\n", ull); } num -= pl; ucp += pl; } pout("\n"); } static void scsiPrintErrorCounterLog(scsi_device * device) { struct scsiErrorCounter errCounterArr[3]; struct scsiErrorCounter * ecp; int found[3] = {0, 0, 0}; if (gReadECounterLPage && (0 == scsiLogSense(device, READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]); found[0] = 1; } if (gWriteECounterLPage && (0 == scsiLogSense(device, WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]); found[1] = 1; } if (gVerifyECounterLPage && (0 == scsiLogSense(device, VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]); ecp = &errCounterArr[2]; for (int k = 0; k < 7; ++k) { if (ecp->gotPC[k] && ecp->counter[k]) { found[2] = 1; break; } } } if (found[0] || found[1] || found[2]) { pout("Error counter log:\n"); pout(" Errors Corrected by Total " "Correction Gigabytes Total\n"); pout(" ECC rereads/ errors " "algorithm processed uncorrected\n"); pout(" fast | delayed rewrites corrected " "invocations [10^9 bytes] errors\n"); json::ref jref = jglb["scsi_error_counter_log"]; for (int k = 0; k < 3; ++k) { if (! found[k]) continue; ecp = &errCounterArr[k]; static const char * const pageNames[3] = {"read: ", "write: ", "verify: "}; static const char * jpageNames[3] = {"read", "write", "verify"}; jout("%s%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64, pageNames[k], ecp->counter[0], ecp->counter[1], ecp->counter[2], ecp->counter[3], ecp->counter[4]); double processed_gb = ecp->counter[5] / 1000000000.0; jout(" %12.3f %8" PRIu64 "\n", processed_gb, ecp->counter[6]); // Error counter log info jref[jpageNames[k]]["errors_corrected_by_eccfast"] = ecp->counter[0]; jref[jpageNames[k]]["errors_corrected_by_eccdelayed"] = ecp->counter[1]; jref[jpageNames[k]]["errors_corrected_by_rereads_rewrites"] = ecp->counter[2]; jref[jpageNames[k]]["total_errors_corrected"] = ecp->counter[3]; jref[jpageNames[k]]["correction_algorithm_invocations"] = ecp->counter[4]; jref[jpageNames[k]]["gigabytes_processed"] = strprintf("%.3f", processed_gb); jref[jpageNames[k]]["total_uncorrected_errors"] = ecp->counter[6]; } } else pout("Error Counter logging not supported\n"); if (gNonMediumELPage && (0 == scsiLogSense(device, NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { struct scsiNonMediumError nme; scsiDecodeNonMediumErrPage(gBuf, &nme); if (nme.gotPC0) pout("\nNon-medium error count: %8" PRIu64 "\n", nme.counterPC0); if (nme.gotTFE_H) pout("Track following error count [Hitachi]: %8" PRIu64 "\n", nme.counterTFE_H); if (nme.gotPE_H) pout("Positioning error count [Hitachi]: %8" PRIu64 "\n", nme.counterPE_H); } if (gLastNErrorEvLPage && (0 == scsiLogSense(device, LAST_N_ERROR_EVENTS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { int num = sg_get_unaligned_be16(gBuf + 2) + 4; int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; unsigned char * ucp = gBuf + 4; num -= 4; if (num < 4) pout("\nNo error events logged\n"); else { pout("\nLast n error events log page\n"); for (int k = num, pl; k > 0; k -= pl, ucp += pl) { if (k < 3) { pout(" <>\n"); break; } pl = ucp[3] + 4; int pc = sg_get_unaligned_be16(ucp + 0); if (pl > 4) { if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) { pout(" Error event %d:\n", pc); pout(" [binary]:\n"); dStrHex((const uint8_t *)ucp + 4, pl - 4, 1); } else if (ucp[2] & 0x1) { pout(" Error event %d:\n", pc); pout(" %.*s\n", pl - 4, (const char *)(ucp + 4)); } else { if (scsi_debugmode > 0) { pout(" Error event %d:\n", pc); pout(" [data counter??]:\n"); dStrHex((const uint8_t *)ucp + 4, pl - 4, 1); } } } } if (truncated) pout(" >>>> log truncated, fetched %d of %d available " "bytes\n", LOG_RESP_LONG_LEN, truncated); } } pout("\n"); } static const char * self_test_code[] = { "Default ", "Background short", "Background long ", "Reserved(3) ", "Abort background", "Foreground short", "Foreground long ", "Reserved(7) " }; static const char * self_test_result[] = { "Completed ", "Aborted (by user command)", "Aborted (device reset ?) ", "Unknown error, incomplete", "Completed, segment failed", "Failed in first segment ", "Failed in second segment ", "Failed in segment --> ", "Reserved(8) ", "Reserved(9) ", "Reserved(10) ", "Reserved(11) ", "Reserved(12) ", "Reserved(13) ", "Reserved(14) ", "Self test in progress ..." }; // See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 . // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or // FAILSMART is returned. static int scsiPrintSelfTest(scsi_device * device) { int num, k, err, durationSec; int noheader = 1; int retval = 0; uint8_t * ucp; uint64_t ull; struct scsi_sense_disect sense_info; static const char * hname = "Self-test"; // check if test is running if (!scsiRequestSense(device, &sense_info) && (sense_info.asc == 0x04 && sense_info.ascq == 0x09 && sense_info.progress != -1)) { pout("%s execution status:\t\t%d%% of test remaining\n", hname, 100 - ((sense_info.progress * 100) / 65535)); } if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf, LOG_RESP_SELF_TEST_LEN, 0))) { print_on(); pout("%s: Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) { print_on(); pout("%s %s, page mismatch\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2); // Log sense page length 0x190 bytes if (num != 0x190) { print_on(); pout("%s %s length is 0x%x not 0x190 bytes\n", hname, logSenStr, num); print_off(); return FAILSMART; } // loop through the twenty possible entries for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) { // timestamp in power-on hours (or zero if test in progress) int n = sg_get_unaligned_be16(ucp + 6); // The spec says "all 20 bytes will be zero if no test" but // DG has found otherwise. So this is a heuristic. if ((0 == n) && (0 == ucp[4])) break; // only print header if needed if (noheader) { pout("SMART %s log\n", hname); pout("Num Test Status segment " "LifeTime LBA_first_err [SK ASC ASQ]\n"); pout(" Description number " "(hours)\n"); noheader=0; } // print parameter code (test number) & self-test code text pout("#%2d %s", sg_get_unaligned_be16(ucp + 0), self_test_code[(ucp[4] >> 5) & 0x7]); // check the self-test result nibble, using the self-test results // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10: int res; switch ((res = ucp[4] & 0xf)) { case 0x3: // an unknown error occurred while the device server // was processing the self-test and the device server // was unable to complete the self-test retval|=FAILSMART; break; case 0x4: // the self-test completed with a failure in a test // segment, and the test segment that failed is not // known retval|=FAILLOG; break; case 0x5: // the first segment of the self-test failed retval|=FAILLOG; break; case 0x6: // the second segment of the self-test failed retval|=FAILLOG; break; case 0x7: // another segment of the self-test failed and which // test is indicated by the contents of the SELF-TEST // NUMBER field retval|=FAILLOG; break; default: break; } pout(" %s", self_test_result[res]); // self-test number identifies test that failed and consists // of either the number of the segment that failed during // the test, or the number of the test that failed and the // number of the segment in which the test was run, using a // vendor-specific method of putting both numbers into a // single byte. if (ucp[5]) pout(" %3d", (int)ucp[5]); else pout(" -"); // print time that the self-test was completed if (n==0 && res==0xf) // self-test in progress pout(" NOW"); else pout(" %5d", n); // construct 8-byte integer address of first failure ull = sg_get_unaligned_be64(ucp + 8); bool is_all_ffs = all_ffs(ucp + 8, 8); // print Address of First Failure, if sensible if ((! is_all_ffs) && (res > 0) && (res < 0xf)) { char buff[32]; // was hex but change to decimal to conform with ATA snprintf(buff, sizeof(buff), "%" PRIu64, ull); // snprintf(buff, sizeof(buff), "0x%" PRIx64, ull); pout("%18s", buff); } else pout(" -"); // if sense key nonzero, then print it, along with // additional sense code and additional sense code qualifier if (ucp[16] & 0xf) pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]); else pout(" [- - -]\n"); } // if header never printed, then there was no output if (noheader) pout("No %ss have been logged\n", hname); else if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, modese_len)) && (durationSec > 0)) { pout("\nLong (extended) %s duration: %d seconds " "[%.1f minutes]\n", hname, durationSec, durationSec / 60.0); } pout("\n"); return retval; } static const char * bms_status[] = { "no scans active", "scan is active", "pre-scan is active", "halted due to fatal error", "halted due to a vendor specific pattern of error", "halted due to medium formatted without P-List", "halted - vendor specific cause", "halted due to temperature out of range", "waiting until BMS interval timer expires", /* 8 */ }; static const char * reassign_status[] = { "Reserved [0x0]", "Require Write or Reassign Blocks command", "Successfully reassigned", "Reserved [0x3]", "Reassignment by disk failed", "Recovered via rewrite in-place", "Reassigned by app, has valid data", "Reassigned by app, has no valid data", "Unsuccessfully reassigned by app", /* 8 */ }; // See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 . // Returns 0 if ok else FAIL* bitmask. Note can have a status entry // and up to 2048 events (although would hope to have less). May set // FAILLOG if serious errors detected (in the future). static int scsiPrintBackgroundResults(scsi_device * device) { int num, j, m, err, truncated; int noheader = 1; int firstresult = 1; int retval = 0; uint8_t * ucp; static const char * hname = "Background scan results"; if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) { print_on(); pout("%s %s, page mismatch\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2) + 4; if (num < 20) { print_on(); pout("%s %s length is %d, no scan status\n", hname, logSenStr, num); print_off(); return FAILSMART; } truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; ucp = gBuf + 4; num -= 4; while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); // pcb = ucp[2]; int pl = ucp[3] + 4; switch (pc) { case 0: if (noheader) { noheader = 0; pout("%s log\n", hname); } pout(" Status: "); if ((pl < 16) || (num < 16)) { pout("\n"); break; } j = ucp[9]; if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0]))) pout("%s\n", bms_status[j]); else pout("unknown [0x%x] background scan status value\n", j); j = sg_get_unaligned_be32(ucp + 4); pout(" Accumulated power on time, hours:minutes %d:%02d " "[%d minutes]\n", (j / 60), (j % 60), j); jglb["power_on_time"]["hours"] = j / 60; jglb["power_on_time"]["minutes"] = j % 60; pout(" Number of background scans performed: %d, ", sg_get_unaligned_be16(ucp + 10)); pout("scan progress: %.2f%%\n", (double)sg_get_unaligned_be16(ucp + 12) * 100.0 / 65536.0); pout(" Number of background medium scans performed: %d\n", sg_get_unaligned_be16(ucp + 14)); break; default: if (noheader) { noheader = 0; pout("\n%s log\n", hname); } if (firstresult) { firstresult = 0; pout("\n # when lba(hex) [sk,asc,ascq] " "reassign_status\n"); } pout(" %3d ", pc); if ((pl < 24) || (num < 24)) { if (pl < 24) pout("parameter length >= 24 expected, got %d\n", pl); break; } j = sg_get_unaligned_be32(ucp + 4); pout("%4d:%02d ", (j / 60), (j % 60)); for (m = 0; m < 8; ++m) pout("%02x", ucp[16 + m]); pout(" [%x,%x,%x] ", ucp[8] & 0xf, ucp[9], ucp[10]); j = (ucp[8] >> 4) & 0xf; if (j < (int)(sizeof(reassign_status) / sizeof(reassign_status[0]))) pout("%s\n", reassign_status[j]); else pout("Reassign status: reserved [0x%x]\n", j); break; } num -= pl; ucp += pl; } if (truncated) pout(" >>>> log truncated, fetched %d of %d available " "bytes\n", LOG_RESP_LONG_LEN, truncated); pout("\n"); return retval; } // See SCSI Block Commands - 3 (SBC-3) rev 27 (draft) section 6.3.6 . // Returns 0 if ok else FAIL* bitmask. Note can have a status entry // and up to 2048 events (although would hope to have less). May set // FAILLOG if serious errors detected (in the future). static int scsiPrintSSMedia(scsi_device * device) { int num, err, truncated; int retval = 0; uint8_t * ucp; static const char * hname = "Solid state media"; if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s: Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) { print_on(); pout("%s %s, page mismatch\n", hname, logSenStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2) + 4; if (num < 12) { print_on(); pout("%s %s length is %d, too short\n", hname, logSenStr, num); print_off(); return FAILSMART; } truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; ucp = gBuf + 4; num -= 4; while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); // pcb = ucp[2]; int pl = ucp[3] + 4; switch (pc) { case 1: if (pl < 8) { print_on(); pout("%s Percentage used endurance indicator parameter " "too short (pl=%d)\n", hname, pl); print_off(); return FAILSMART; } jout("Percentage used endurance indicator: %d%%\n", ucp[7]); jglb["scsi_percentage_used_endurance_indicator"] = ucp[7]; default: /* ignore other parameter codes */ break; } num -= pl; ucp += pl; } return retval; } static int scsiPrintFormatStatus(scsi_device * device) { bool is_count; int k, num, err, truncated; int retval = 0; uint64_t ull; uint8_t * ucp; uint8_t * xp; const char * jout_str; const char * jglb_str; static const char * hname = "Format Status"; static const char * jname = "format_status"; if ((err = scsiLogSense(device, FORMAT_STATUS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); jout("%s: Failed [%s]\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != FORMAT_STATUS_LPAGE) { print_on(); jout("%s %s, page mismatch\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2) + 4; if (num < 12) { print_on(); jout("%s %s length is %d, too short\n", hname, logSenStr, num); print_off(); return FAILSMART; } truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; ucp = gBuf + 4; num -= 4; while (num > 3) { int pc = sg_get_unaligned_be16(ucp + 0); // pcb = ucp[2]; int pl = ucp[3] + 4; is_count = true; jout_str = ""; jglb_str = "x"; switch (pc) { case 0: if (scsi_debugmode > 1) { if (pl < 5) jout("Format data out: \n"); else { if (all_ffs(ucp + 4, pl - 4)) jout("Format data out: \n"); else { jout("Format data out:\n"); dStrHex((const uint8_t *)ucp + 4, pl - 4, 0); } } } is_count = false; break; case 1: jout_str = "Grown defects during certification"; jglb_str = "grown_defects_during_cert"; break; case 2: jout_str = "Total blocks reassigned during format"; jglb_str = "blocks_reassigned_during_format"; break; case 3: jout_str = "Total new blocks reassigned"; jglb_str = "total_new_block_since_format"; break; case 4: jout_str = "Power on minutes since format"; jglb_str = "power_on_minutes_since_format"; break; default: if (scsi_debugmode > 3) { pout(" Unknown Format parameter code = 0x%x\n", pc); dStrHex((const uint8_t *)ucp, pl, 0); } is_count = false; break; } if (is_count) { k = pl - 4; xp = ucp + 4; if (all_ffs(xp, k)) { pout("%s \n", jout_str); } else { if (k > (int)sizeof(ull)) { xp += (k - sizeof(ull)); k = sizeof(ull); } ull = sg_get_unaligned_be(k, xp); jout("%s = %" PRIu64 "\n", jout_str, ull); jglb[jname][jglb_str] = ull; } } else num -= pl; ucp += pl; } return retval; } static void show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val) { unsigned int u; switch (peis) { case 0: pout(" No event\n"); break; case 0x1: pout(" Invalid word count: %u\n", val); break; case 0x2: pout(" Running disparity error count: %u\n", val); break; case 0x3: pout(" Loss of dword synchronization count: %u\n", val); break; case 0x4: pout(" Phy reset problem count: %u\n", val); break; case 0x5: pout(" Elasticity buffer overflow count: %u\n", val); break; case 0x6: pout(" Received ERROR count: %u\n", val); break; case 0x20: pout(" Received address frame error count: %u\n", val); break; case 0x21: pout(" Transmitted abandon-class OPEN_REJECT count: %u\n", val); break; case 0x22: pout(" Received abandon-class OPEN_REJECT count: %u\n", val); break; case 0x23: pout(" Transmitted retry-class OPEN_REJECT count: %u\n", val); break; case 0x24: pout(" Received retry-class OPEN_REJECT count: %u\n", val); break; case 0x25: pout(" Received AIP (WAITING ON PARTIAL) count: %u\n", val); break; case 0x26: pout(" Received AIP (WAITING ON CONNECTION) count: %u\n", val); break; case 0x27: pout(" Transmitted BREAK count: %u\n", val); break; case 0x28: pout(" Received BREAK count: %u\n", val); break; case 0x29: pout(" Break timeout count: %u\n", val); break; case 0x2a: pout(" Connection count: %u\n", val); break; case 0x2b: pout(" Peak transmitted pathway blocked count: %u\n", val & 0xff); pout(" Peak value detector threshold: %u\n", thresh_val & 0xff); break; case 0x2c: u = val & 0xffff; if (u < 0x8000) pout(" Peak transmitted arbitration wait time (us): " "%u\n", u); else pout(" Peak transmitted arbitration wait time (ms): " "%u\n", 33 + (u - 0x8000)); u = thresh_val & 0xffff; if (u < 0x8000) pout(" Peak value detector threshold (us): %u\n", u); else pout(" Peak value detector threshold (ms): %u\n", 33 + (u - 0x8000)); break; case 0x2d: pout(" Peak arbitration time (us): %u\n", val); pout(" Peak value detector threshold: %u\n", thresh_val); break; case 0x2e: pout(" Peak connection time (us): %u\n", val); pout(" Peak value detector threshold: %u\n", thresh_val); break; case 0x40: pout(" Transmitted SSP frame count: %u\n", val); break; case 0x41: pout(" Received SSP frame count: %u\n", val); break; case 0x42: pout(" Transmitted SSP frame error count: %u\n", val); break; case 0x43: pout(" Received SSP frame error count: %u\n", val); break; case 0x44: pout(" Transmitted CREDIT_BLOCKED count: %u\n", val); break; case 0x45: pout(" Received CREDIT_BLOCKED count: %u\n", val); break; case 0x50: pout(" Transmitted SATA frame count: %u\n", val); break; case 0x51: pout(" Received SATA frame count: %u\n", val); break; case 0x52: pout(" SATA flow control buffer overflow count: %u\n", val); break; case 0x60: pout(" Transmitted SMP frame count: %u\n", val); break; case 0x61: pout(" Received SMP frame count: %u\n", val); break; case 0x63: pout(" Received SMP frame error count: %u\n", val); break; default: break; } } static void show_sas_port_param(unsigned char * ucp, int param_len) { int j, m, nphys, t, sz, spld_len; unsigned char * vcp; char s[64]; sz = sizeof(s); // pcb = ucp[2]; t = sg_get_unaligned_be16(ucp + 0); pout("relative target port id = %d\n", t); pout(" generation code = %d\n", ucp[6]); nphys = ucp[7]; pout(" number of phys = %d\n", nphys); for (j = 0, vcp = ucp + 8; j < (param_len - 8); vcp += spld_len, j += spld_len) { pout(" phy identifier = %d\n", vcp[1]); spld_len = vcp[3]; if (spld_len < 44) spld_len = 48; /* in SAS-1 and SAS-1.1 vcp[3]==0 */ else spld_len += 4; t = ((0x70 & vcp[4]) >> 4); switch (t) { case 0: snprintf(s, sz, "no device attached"); break; case 1: snprintf(s, sz, "SAS or SATA device"); break; case 2: snprintf(s, sz, "expander device"); break; case 3: snprintf(s, sz, "expander device (fanout)"); break; default: snprintf(s, sz, "reserved [%d]", t); break; } pout(" attached device type: %s\n", s); t = 0xf & vcp[4]; switch (t) { case 0: snprintf(s, sz, "unknown"); break; case 1: snprintf(s, sz, "power on"); break; case 2: snprintf(s, sz, "hard reset"); break; case 3: snprintf(s, sz, "SMP phy control function"); break; case 4: snprintf(s, sz, "loss of dword synchronization"); break; case 5: snprintf(s, sz, "mux mix up"); break; case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA"); break; case 7: snprintf(s, sz, "break timeout timer expired"); break; case 8: snprintf(s, sz, "phy test function stopped"); break; case 9: snprintf(s, sz, "expander device reduced functionality"); break; default: snprintf(s, sz, "reserved [0x%x]", t); break; } pout(" attached reason: %s\n", s); t = (vcp[5] & 0xf0) >> 4; switch (t) { case 0: snprintf(s, sz, "unknown"); break; case 1: snprintf(s, sz, "power on"); break; case 2: snprintf(s, sz, "hard reset"); break; case 3: snprintf(s, sz, "SMP phy control function"); break; case 4: snprintf(s, sz, "loss of dword synchronization"); break; case 5: snprintf(s, sz, "mux mix up"); break; case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA"); break; case 7: snprintf(s, sz, "break timeout timer expired"); break; case 8: snprintf(s, sz, "phy test function stopped"); break; case 9: snprintf(s, sz, "expander device reduced functionality"); break; default: snprintf(s, sz, "reserved [0x%x]", t); break; } pout(" reason: %s\n", s); t = (0xf & vcp[5]); switch (t) { case 0: snprintf(s, sz, "phy enabled; unknown"); break; case 1: snprintf(s, sz, "phy disabled"); break; case 2: snprintf(s, sz, "phy enabled; speed negotiation failed"); break; case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state"); break; case 4: snprintf(s, sz, "phy enabled; port selector"); break; case 5: snprintf(s, sz, "phy enabled; reset in progress"); break; case 6: snprintf(s, sz, "phy enabled; unsupported phy attached"); break; case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break; case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break; case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break; case 0xb: snprintf(s, sz, "phy enabled; 12 Gbps"); break; default: snprintf(s, sz, "reserved [%d]", t); break; } pout(" negotiated logical link rate: %s\n", s); pout(" attached initiator port: ssp=%d stp=%d smp=%d\n", !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2)); pout(" attached target port: ssp=%d stp=%d smp=%d\n", !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2)); if (!dont_print_serial_number) { uint64_t ull = sg_get_unaligned_be64(vcp + 8); pout(" SAS address = 0x%" PRIx64 "\n", ull); ull = sg_get_unaligned_be64(vcp + 16); pout(" attached SAS address = 0x%" PRIx64 "\n", ull); } pout(" attached phy identifier = %d\n", vcp[24]); unsigned int ui = sg_get_unaligned_be32(vcp + 32); pout(" Invalid DWORD count = %u\n", ui); ui = sg_get_unaligned_be32(vcp + 36); pout(" Running disparity error count = %u\n", ui); ui = sg_get_unaligned_be32(vcp + 40); pout(" Loss of DWORD synchronization = %u\n", ui); ui = sg_get_unaligned_be32(vcp + 44); pout(" Phy reset problem = %u\n", ui); if (spld_len > 51) { int num_ped; unsigned char * xcp; num_ped = vcp[51]; if (num_ped > 0) pout(" Phy event descriptors:\n"); xcp = vcp + 52; for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) { int peis; unsigned int pvdt; peis = xcp[3]; ui = sg_get_unaligned_be32(xcp + 4); pvdt = sg_get_unaligned_be32(xcp + 8); show_sas_phy_event_info(peis, ui, pvdt); } } } } // Returns 1 if okay, 0 if non SAS descriptors static int show_protocol_specific_page(unsigned char * resp, int len) { int k, num; unsigned char * ucp; num = len - 4; for (k = 0, ucp = resp + 4; k < num; ) { int param_len = ucp[3] + 4; if (SCSI_TPROTO_SAS != (0xf & ucp[4])) return 0; /* only decode SAS log page */ if (0 == k) pout("Protocol Specific port log page for SAS SSP\n"); show_sas_port_param(ucp, param_len); k += param_len; ucp += param_len; } pout("\n"); return 1; } // See Serial Attached SCSI (SPL-3) (e.g. revision 6g) the Protocol Specific // log page [0x18]. Returns 0 if ok else FAIL* bitmask. static int scsiPrintSasPhy(scsi_device * device, int reset) { int num, err; static const char * hname = "Protocol specific port"; if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { print_on(); pout("%s %s Failed [%s]\n\n", __func__, logSenStr, scsiErrString(err)); print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) { print_on(); pout("%s %s, page mismatch\n\n", hname, logSenRspStr); print_off(); return FAILSMART; } // compute page length num = sg_get_unaligned_be16(gBuf + 2); if (1 != show_protocol_specific_page(gBuf, num + 4)) { print_on(); pout("Only support %s log page on SAS devices\n\n", hname); print_off(); return FAILSMART; } if (reset) { if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */, PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) { print_on(); pout("%s Log Select (reset) Failed [%s]\n\n", __func__, scsiErrString(err)); print_off(); return FAILSMART; } } return 0; } static const char * peripheral_dt_arr[32] = { "disk", "tape", "printer", "processor", "optical disk(4)", "CD/DVD", "scanner", "optical disk(7)", "medium changer", "communications", "graphics(10)", "graphics(11)", "storage array", "enclosure", "simplified disk", "optical card reader", "reserved [0x10]", "object based storage", "automation/driver interface", "security manager device", "host managed zoned block device", "reserved [0x15]", "reserved [0x16]", "reserved [0x17]", "reserved [0x18]", "reserved [0x19]", "reserved [0x1a]", "reserved [0x1b]", "reserved [0x1c]", "reserved [0x1d]", "well known logical unit", "unknown or no device type", }; /* Symbolic indexes to this array SCSI_TPROTO_* in scscmds.h */ static const char * transport_proto_arr[] = { "Fibre channel (FCP-2)", "Parallel SCSI (SPI-4)", "SSA", "IEEE 1394 (SBP-2)", "RDMA (SRP)", "iSCSI", "SAS (SPL-3)", "ADT", "ATA (ACS-2)", "UAS", "SOP", "PCIe", "0xc", "0xd", "0xe", "None given [0xf]" }; /* Returns 0 on success, 1 on general error and 2 for early, clean exit */ static int scsiGetDriveInfo(scsi_device * device, uint8_t * peripheral_type, bool all) { struct scsi_iec_mode_page iec; int err, iec_err, len, req_len, avail_len; bool is_tape = false; int peri_dt = 0; int transport = -1; int form_factor = 0; int haw_zbc = 0; int protect = 0; memset(gBuf, 0, 96); req_len = 36; if ((err = scsiStdInquiry(device, gBuf, req_len))) { print_on(); pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err)); pout("Retrying with a 64 byte Standard Inquiry\n"); print_off(); /* Marvell controllers fail with 36 byte StdInquiry, but 64 is ok */ req_len = 64; if ((err = scsiStdInquiry(device, gBuf, req_len))) { print_on(); pout("Standard Inquiry (64 bytes) failed [%s]\n", scsiErrString(err)); print_off(); return 1; } } avail_len = gBuf[4] + 5; len = (avail_len < req_len) ? avail_len : req_len; peri_dt = gBuf[0] & 0x1f; *peripheral_type = peri_dt; if ((SCSI_PT_SEQUENTIAL_ACCESS == peri_dt) || (SCSI_PT_MEDIUM_CHANGER == peri_dt)) is_tape = true; if (len < 36) { print_on(); pout("Short INQUIRY response, skip product id\n"); print_off(); return 1; } // Upper bits of version bytes were used in older standards // Only interested in SPC-4 (0x6) and SPC-5 (assumed to be 0x7) scsi_version = gBuf[2] & 0x7; if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) { char product[16+1], revision[4+1]; scsi_format_id_string(scsi_vendor, &gBuf[8], 8); scsi_format_id_string(product, &gBuf[16], 16); scsi_format_id_string(revision, &gBuf[32], 4); pout("=== START OF INFORMATION SECTION ===\n"); jout("Vendor: %.8s\n", scsi_vendor); jglb["vendor"] = scsi_vendor; jout("Product: %.16s\n", product); jglb["product"] = product; jglb["model_name"] = strprintf("%s%s%s", scsi_vendor, (*scsi_vendor && *product ? " " : ""), product); if (gBuf[32] >= ' ') { jout("Revision: %.4s\n", revision); // jglb["firmware_version"] = revision; jglb["revision"] = revision; /* could be a hardware rev */ } if ((scsi_version > 0x3) && (scsi_version < 0x8)) { char sv_arr[8]; snprintf(sv_arr, sizeof(sv_arr), "SPC-%d", scsi_version - 2); jout("Compliance: %s\n", sv_arr); jglb["scsi_version"] = sv_arr; } } if (!*device->get_req_type()/*no type requested*/ && (0 == strncmp((char *)&gBuf[8], "ATA", 3))) { pout("\nProbable ATA device behind a SAT layer\n" "Try an additional '-d ata' or '-d sat' argument.\n"); return 2; } if (! all) return 0; protect = gBuf[5] & 0x1; /* from and including SPC-3 */ if (! is_tape) { /* assume disk if not tape drive (or tape changer) */ struct scsi_readcap_resp srr; int lbpme = -1; int lbprz = -1; unsigned char lb_prov_resp[8]; uint64_t capacity = scsiGetSize(device, false /*avoid_rcap16 */, &srr); if (capacity) { char cap_str[64], si_str[64]; format_with_thousands_sep(cap_str, sizeof(cap_str), capacity); format_capacity(si_str, sizeof(si_str), capacity); jout("User Capacity: %s bytes [%s]\n", cap_str, si_str); if (srr.lb_size) jglb["user_capacity"]["blocks"].set_unsafe_uint64(capacity / srr.lb_size); jglb["user_capacity"]["bytes"].set_unsafe_uint64(capacity); jout("Logical block size: %u bytes\n", srr.lb_size); jglb["logical_block_size"] = srr.lb_size; if (protect || srr.lb_p_pb_exp) { if (srr.lb_p_pb_exp > 0) { unsigned pb_size = srr.lb_size * (1 << srr.lb_p_pb_exp); jout("Physical block size: %u bytes\n", pb_size); jglb["physical_block_size"] = pb_size; if (srr.l_a_lba > 0) // not common so cut the clutter pout("Lowest aligned LBA: %u\n", srr.l_a_lba); } if (srr.prot_type > 0) { switch (srr.prot_type) { case 1 : pout("Formatted with type 1 protection\n"); break; case 2 : pout("Formatted with type 2 protection\n"); break; case 3 : pout("Formatted with type 3 protection\n"); break; default: pout("Formatted with unknown protection type [%d]\n", srr.prot_type); break; } unsigned p_i_per_lb = (1 << srr.p_i_exp); const unsigned pi_sz = 8; /* ref-tag(4 bytes), app-tag(2), tag-mask(2) */ if (p_i_per_lb > 1) pout("%d protection information intervals per " "logical block\n", p_i_per_lb); pout("%d bytes of protection information per logical " "block\n", pi_sz * p_i_per_lb); } /* Pick up some LB provisioning info since its available */ lbpme = (int)srr.lbpme; lbprz = (int)srr.lbprz; } } /* Thin Provisioning VPD page renamed Logical Block Provisioning VPD * page in sbc3r25; some fields changed their meaning so that the * new page covered both thin and resource provisioned LUs. */ if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING, lb_prov_resp, sizeof(lb_prov_resp))) { int prov_type = lb_prov_resp[6] & 0x7; /* added sbc3r27 */ int vpd_lbprz = ((lb_prov_resp[5] >> 2) & 0x7); /* sbc4r07 */ if (-1 == lbprz) lbprz = vpd_lbprz; else if ((0 == vpd_lbprz) && (1 == lbprz)) ; /* vpd_lbprz introduced in sbc3r27, expanded in sbc4r07 */ else lbprz = vpd_lbprz; switch (prov_type) { case 0: if (lbpme <= 0) { pout("LU is fully provisioned"); if (lbprz) pout(" [LBPRZ=%d]\n", lbprz); else pout("\n"); } else pout("LB provisioning type: not reported [LBPME=1, " "LBPRZ=%d]\n", lbprz); break; case 1: pout("LU is resource provisioned, LBPRZ=%d\n", lbprz); break; case 2: pout("LU is thin provisioned, LBPRZ=%d\n", lbprz); break; default: pout("LU provisioning type reserved [%d], LBPRZ=%d\n", prov_type, lbprz); break; } } else if (1 == lbpme) { if (scsi_debugmode > 0) pout("rcap_16 sets LBPME but no LB provisioning VPD page\n"); pout("Logical block provisioning enabled, LBPRZ=%d\n", lbprz); } int rpm = scsiGetRPM(device, modese_len, &form_factor, &haw_zbc); if (rpm >= 0) { if (0 == rpm) ; // Not reported else if (1 == rpm) jout("Rotation Rate: Solid State Device\n"); else if ((rpm <= 0x400) || (0xffff == rpm)) ; // Reserved else jout("Rotation Rate: %d rpm\n", rpm); jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm); } if (form_factor > 0) { const char * cp = NULL; switch (form_factor) { case 1: cp = "5.25"; break; case 2: cp = "3.5"; break; case 3: cp = "2.5"; break; case 4: cp = "1.8"; break; case 5: cp = "< 1.8"; break; } jglb["form_factor"]["scsi_value"] = form_factor; if (cp) { jout("Form Factor: %s inches\n", cp); jglb["form_factor"]["name"] = strprintf("%s inches", cp); } } if (haw_zbc > 0) pout("Host aware zoned block capable\n"); } /* Do this here to try and detect badly conforming devices (some USB keys) that will lock up on a InquiryVpd or log sense or ... */ if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) { if (SIMPLE_ERR_BAD_RESP == iec_err) { pout(">> Terminate command early due to bad response to IEC " "mode page\n"); print_off(); gIecMPage = 0; return 1; } } else modese_len = iec.modese_len; if (! dont_print_serial_number) { if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION, gBuf, 252))) { char s[256]; len = gBuf[3]; scsi_decode_lu_dev_id(gBuf + 4, len, s, sizeof(s), &transport); if (strlen(s) > 0) pout("Logical Unit id: %s\n", s); } else if (scsi_debugmode > 0) { print_on(); if (SIMPLE_ERR_BAD_RESP == err) pout("Vital Product Data (VPD) bit ignored in INQUIRY\n"); else pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err); print_off(); } if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_UNIT_SERIAL_NUMBER, gBuf, 252))) { char serial[256]; len = gBuf[3]; gBuf[4 + len] = '\0'; scsi_format_id_string(serial, &gBuf[4], len); jout("Serial number: %s\n", serial); jglb["serial_number"] = serial; } else if (scsi_debugmode > 0) { print_on(); if (SIMPLE_ERR_BAD_RESP == err) pout("Vital Product Data (VPD) bit ignored in INQUIRY\n"); else pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err); print_off(); } } // print SCSI peripheral device type jglb["device_type"]["scsi_value"] = peri_dt; if (peri_dt < (int)(sizeof(peripheral_dt_arr) / sizeof(peripheral_dt_arr[0]))) { jout("Device type: %s\n", peripheral_dt_arr[peri_dt]); jglb["device_type"]["name"] = peripheral_dt_arr[peri_dt]; } else jout("Device type: <%d>\n", peri_dt); // See if transport protocol is known if (transport < 0) transport = scsiFetchTransportProtocol(device, modese_len); if ((transport >= 0) && (transport <= 0xf)) pout("Transport protocol: %s\n", transport_proto_arr[transport]); // print current time and date and timezone time_t now = time(0); char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now); jout("Local Time is: %s\n", timedatetz); jglb["local_time"]["time_t"] = now; jglb["local_time"]["asctime"] = timedatetz; // See if unit accepts SCSI commands from us if ((err = scsiTestUnitReady(device))) { if (SIMPLE_ERR_NOT_READY == err) { print_on(); if (!is_tape) pout("device is NOT READY (e.g. spun down, busy)\n"); else pout("device is NOT READY (e.g. no tape)\n"); print_off(); } else if (SIMPLE_ERR_NO_MEDIUM == err) { print_on(); if (is_tape) pout("NO tape present in drive\n"); else pout("NO MEDIUM present in device\n"); print_off(); } else if (SIMPLE_ERR_BECOMING_READY == err) { print_on(); pout("device becoming ready (wait)\n"); print_off(); } else { print_on(); pout("device Test Unit Ready [%s]\n", scsiErrString(err)); print_off(); } if (! is_tape) { int returnval = 0; // TODO: exit with FAILID if failuretest returns failuretest(MANDATORY_CMD, returnval|=FAILID); } } if (iec_err) { if (!is_tape) { print_on(); pout("SMART support is: Unavailable - device lacks SMART " "capability.\n"); if (scsi_debugmode > 0) pout(" [%s]\n", scsiErrString(iec_err)); print_off(); } gIecMPage = 0; return 0; } if (!is_tape) pout("SMART support is: Available - device has SMART capability.\n" "SMART support is: %s\n", (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled"); pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? "Temperature Warning: Enabled" : "Temperature Warning: Disabled or Not Supported"); return 0; } static int scsiSmartEnable(scsi_device * device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { print_on(); pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); print_off(); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) { print_on(); pout("unable to enable Exception control and warning [%s]\n", scsiErrString(err)); print_off(); return 1; } /* Need to refetch 'iec' since could be modified by previous call */ if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); return 1; } else modese_len = iec.modese_len; pout("Informational Exceptions (SMART) %s\n", scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled"); pout("Temperature warning %s\n", scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); return 0; } static int scsiSmartDisable(scsi_device * device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { print_on(); pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); print_off(); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) { print_on(); pout("unable to disable Exception control and warning [%s]\n", scsiErrString(err)); print_off(); return 1; } /* Need to refetch 'iec' since could be modified by previous call */ if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); return 1; } else modese_len = iec.modese_len; pout("Informational Exceptions (SMART) %s\n", scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled"); pout("Temperature warning %s\n", scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); return 0; } static void scsiPrintTemp(scsi_device * device) { uint8_t temp = 255; uint8_t trip = 255; if (scsiGetTemp(device, &temp, &trip)) return; if (255 == temp) pout("Current Drive Temperature: \n"); else { jout("Current Drive Temperature: %d C\n", temp); jglb["temperature"]["current"] = temp; } if (255 == trip) pout("Drive Trip Temperature: \n"); else { jout("Drive Trip Temperature: %d C\n", trip); jglb["temperature"]["drive_trip"] = trip; } pout("\n"); } /* Main entry point used by smartctl command. Return 0 for success */ int scsiPrintMain(scsi_device * device, const scsi_print_options & options) { int checkedSupportedLogPages = 0; uint8_t peripheral_type = 0; int returnval = 0; int res, durationSec; struct scsi_sense_disect sense_info; bool is_disk; bool is_tape; bool any_output = options.drive_info; if (supported_vpd_pages_p) { delete supported_vpd_pages_p; supported_vpd_pages_p = NULL; } supported_vpd_pages_p = new supported_vpd_pages(device); res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info); if (res) { if (2 == res) return 0; else failuretest(MANDATORY_CMD, returnval |= FAILID); any_output = true; } is_disk = ((SCSI_PT_DIRECT_ACCESS == peripheral_type) || (SCSI_PT_HOST_MANAGED == peripheral_type)); is_tape = ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) || (SCSI_PT_MEDIUM_CHANGER == peripheral_type)); short int wce = -1, rcd = -1; // Print read look-ahead status for disks if (options.get_rcd || options.get_wce) { if (is_disk) { res = scsiGetSetCache(device, modese_len, &wce, &rcd); if (options.get_rcd) pout("Read Cache is: %s\n", res ? "Unavailable" : // error rcd ? "Disabled" : "Enabled"); if (options.get_wce) pout("Writeback Cache is: %s\n", res ? "Unavailable" : // error !wce ? "Disabled" : "Enabled"); } } else any_output = true; if (options.drive_info) pout("\n"); // START OF THE ENABLE/DISABLE SECTION OF THE CODE if (options.smart_disable || options.smart_enable || options.smart_auto_save_disable || options.smart_auto_save_enable) pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n"); if (options.smart_enable) { if (scsiSmartEnable(device)) failuretest(MANDATORY_CMD, returnval |= FAILSMART); any_output = true; } if (options.smart_disable) { if (scsiSmartDisable(device)) failuretest(MANDATORY_CMD,returnval |= FAILSMART); any_output = true; } if (options.smart_auto_save_enable) { if (scsiSetControlGLTSD(device, 0, modese_len)) { pout("Enable autosave (clear GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Autosave enabled (GLTSD bit cleared).\n"); any_output = true; } // Enable/Disable write cache if (options.set_wce && is_disk) { short int enable = wce = (options.set_wce > 0); rcd = -1; if (scsiGetSetCache(device, modese_len, &wce, &rcd)) { pout("Write cache %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Write cache %sabled\n", (enable ? "en" : "dis")); any_output = true; } // Enable/Disable read cache if (options.set_rcd && is_disk) { short int enable = (options.set_rcd > 0); rcd = !enable; wce = -1; if (scsiGetSetCache(device, modese_len, &wce, &rcd)) { pout("Read cache %sable failed: %s\n", (enable ? "en" : "dis"), device->get_errmsg()); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Read cache %sabled\n", (enable ? "en" : "dis")); any_output = true; } if (options.smart_auto_save_disable) { if (scsiSetControlGLTSD(device, 1, modese_len)) { pout("Disable autosave (set GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } else pout("Autosave disabled (GLTSD bit set).\n"); any_output = true; } if (options.smart_disable || options.smart_enable || options.smart_auto_save_disable || options.smart_auto_save_enable) pout("\n"); // END OF THE ENABLE/DISABLE SECTION OF THE CODE // START OF READ-ONLY OPTIONS APART FROM -V and -i if (options.smart_check_status || options.smart_ss_media_log || options.smart_vendor_attrib || options.smart_error_log || options.smart_selftest_log || options.smart_background_log || options.sasphy) pout("=== START OF READ SMART DATA SECTION ===\n"); if (options.smart_check_status) { scsiGetSupportedLogPages(device); checkedSupportedLogPages = 1; if (is_tape) { if (gTapeAlertsLPage) { if (options.drive_info) pout("TapeAlert Supported\n"); if (-1 == scsiGetTapeAlertsData(device, peripheral_type)) failuretest(OPTIONAL_CMD, returnval |= FAILSMART); } else pout("TapeAlert Not Supported\n"); } else { /* disk, cd/dvd, enclosure, etc */ if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) { if (-2 == res) returnval |= FAILSTATUS; else returnval |= FAILSMART; } } any_output = true; } if (is_disk && options.smart_ss_media_log) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); res = 0; if (gSSMediaLPage) res = scsiPrintSSMedia(device); if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); if (gFormatStatusLPage) res = scsiPrintFormatStatus(device); if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); any_output = true; } if (options.smart_vendor_attrib) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); if (gTempLPage) scsiPrintTemp(device); if (gStartStopLPage) scsiGetStartStopData(device); if (is_disk) { scsiPrintGrownDefectListLen(device); if (gSeagateCacheLPage) scsiPrintSeagateCacheLPage(device); if (gSeagateFactoryLPage) scsiPrintSeagateFactoryLPage(device); } any_output = true; } if (options.smart_error_log) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); scsiPrintErrorCounterLog(device); if (gPendDefectsLPage) scsiPrintPendingDefectsLPage(device); if (1 == scsiFetchControlGLTSD(device, modese_len, 1)) pout("\n[GLTSD (Global Logging Target Save Disable) set. " "Enable Save with '-S on']\n"); any_output = true; } if (options.smart_selftest_log) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); res = 0; if (gSelfTestLPage) res = scsiPrintSelfTest(device); else { pout("Device does not support Self Test logging\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); any_output = true; } if (options.smart_background_log && is_disk) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(device); res = 0; if (gBackgroundResultsLPage) res = scsiPrintBackgroundResults(device); else { pout("Device does not support Background scan results logging\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); any_output = true; } if (options.smart_default_selftest) { if (scsiSmartDefaultSelfTest(device)) return returnval | FAILSMART; pout("Default Self Test Successful\n"); any_output = true; } if (options.smart_short_cap_selftest) { if (scsiSmartShortCapSelfTest(device)) return returnval | FAILSMART; pout("Short Foreground Self Test Successful\n"); any_output = true; } // check if another test is running if (options.smart_short_selftest || options.smart_extend_selftest) { if (!scsiRequestSense(device, &sense_info) && (sense_info.asc == 0x04 && sense_info.ascq == 0x09)) { if (!options.smart_selftest_force) { pout("Can't start self-test without aborting current test"); if (sense_info.progress != -1) pout(" (%d%% remaining)", 100 - sense_info.progress * 100 / 65535); pout(",\nadd '-t force' option to override, or run " "'smartctl -X' to abort test.\n"); return -1; } else scsiSmartSelfTestAbort(device); } } if (options.smart_short_selftest) { if (scsiSmartShortSelfTest(device)) return returnval | FAILSMART; pout("Short Background Self Test has begun\n"); pout("Use smartctl -X to abort test\n"); any_output = true; } if (options.smart_extend_selftest) { if (scsiSmartExtendSelfTest(device)) return returnval | FAILSMART; pout("Extended Background Self Test has begun\n"); if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, modese_len)) && (durationSec > 0)) { time_t t = time(NULL); t += durationSec; pout("Please wait %d minutes for test to complete.\n", durationSec / 60); pout("Estimated completion time: %s\n", ctime(&t)); } pout("Use smartctl -X to abort test\n"); any_output = true; } if (options.smart_extend_cap_selftest) { if (scsiSmartExtendCapSelfTest(device)) return returnval | FAILSMART; pout("Extended Foreground Self Test Successful\n"); } if (options.smart_selftest_abort) { if (scsiSmartSelfTestAbort(device)) return returnval | FAILSMART; pout("Self Test returned without error\n"); any_output = true; } if (options.sasphy && gProtocolSpecificLPage) { if (scsiPrintSasPhy(device, options.sasphy_reset)) return returnval | FAILSMART; any_output = true; } if (!any_output) pout("SCSI device successfully opened\n\nUse 'smartctl -a' (or '-x') " "to print SMART (and more) information\n\n"); return returnval; }