/* * scsiprint.cpp * * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2002-6 Bruce Allen * Copyright (C) 2000 Michael Cornwell * * Additional SCSI work: * Copyright (C) 2003-6 Douglas Gilbert * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * You should have received a copy of the GNU General Public License * (for example COPYING); if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems * Research Center), Jack Baskin School of Engineering, University of * California, Santa Cruz. http://ssrc.soe.ucsc.edu/ * */ #include #include #include #include #include "config.h" #include "int64.h" #include "extern.h" #include "scsicmds.h" #include "scsiprint.h" #include "smartctl.h" #include "utility.h" #include "scsiata.h" #define GBUF_SIZE 65535 const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.119 2006/11/12 04:47:23 dpgilbert Exp $" CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; // control block which points to external global control variables extern smartmonctrl *con; // to hold onto exit code for atexit routine extern int exitstatus; UINT8 gBuf[GBUF_SIZE]; #define LOG_RESP_LEN 252 #define LOG_RESP_LONG_LEN 16384 #define LOG_RESP_TAPE_ALERT_LEN 0x144 /* Log pages supported */ static int gSmartLPage = 0; /* Informational Exceptions log page */ static int gTempLPage = 0; static int gSelfTestLPage = 0; static int gStartStopLPage = 0; static int gReadECounterLPage = 0; static int gWriteECounterLPage = 0; static int gVerifyECounterLPage = 0; static int gNonMediumELPage = 0; static int gLastNErrorLPage = 0; static int gBackgroundResultsLPage = 0; static int gTapeAlertsLPage = 0; static int gSeagateCacheLPage = 0; static int gSeagateFactoryLPage = 0; /* Mode pages supported */ static int gIecMPage = 1; /* N.B. assume it until we know otherwise */ /* Remember last successful mode sense/select command */ static int modese_len = 0; // Compares failure type to policy in effect, and either exits or // simply returns to the calling routine. extern void failuretest(int type, int returnvalue); static void scsiGetSupportedLogPages(int device) { int i, err; if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, LOG_RESP_LEN, 0))) { if (con->reportscsiioctl > 0) pout("Log Sense for supported pages failed [%s]\n", scsiErrString(err)); return; } for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) { switch (gBuf[i]) { case READ_ERROR_COUNTER_LPAGE: gReadECounterLPage = 1; break; case WRITE_ERROR_COUNTER_LPAGE: gWriteECounterLPage = 1; break; case VERIFY_ERROR_COUNTER_LPAGE: gVerifyECounterLPage = 1; break; case LAST_N_ERROR_LPAGE: gLastNErrorLPage = 1; break; case NON_MEDIUM_ERROR_LPAGE: gNonMediumELPage = 1; break; case TEMPERATURE_LPAGE: gTempLPage = 1; break; case STARTSTOP_CYCLE_COUNTER_LPAGE: gStartStopLPage = 1; break; case SELFTEST_RESULTS_LPAGE: gSelfTestLPage = 1; break; case IE_LPAGE: gSmartLPage = 1; break; case BACKGROUND_RESULTS_LPAGE: gBackgroundResultsLPage = 1; break; case TAPE_ALERTS_LPAGE: gTapeAlertsLPage = 1; break; case SEAGATE_CACHE_LPAGE: gSeagateCacheLPage = 1; break; case SEAGATE_FACTORY_LPAGE: gSeagateFactoryLPage = 1; break; default: break; } } } /* 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(int device, int attribs) { UINT8 asc; UINT8 ascq; UINT8 currenttemp = 0; UINT8 triptemp = 0; const char * cp; int err = 0; PRINT_ON(con); if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq, ¤ttemp, &triptemp)) { /* error message already announced */ PRINT_OFF(con); return -1; } PRINT_OFF(con); cp = scsiGetIEString(asc, ascq); if (cp) { err = -2; PRINT_ON(con); pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); PRINT_OFF(con); } else if (gIecMPage) pout("SMART Health Status: OK\n"); if (attribs && !gTempLPage) { if (currenttemp || triptemp) pout("\n"); if (currenttemp) { if (255 != currenttemp) pout("Current Drive Temperature: %d C\n", currenttemp); else pout("Current Drive Temperature: \n"); } if (triptemp) pout("Drive Trip Temperature: %d C\n", triptemp); } return err; } // Returns number of logged errors or zero if none or -1 if fetching // TapeAlerts fails static char *severities = "CWI"; static int scsiGetTapeAlertsData(int device, int peripheral_type) { unsigned short pagelength; unsigned short parametercode; int i, err; char *s; const char *ts; int failures = 0; PRINT_ON(con); if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf, LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) { pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err)); PRINT_OFF(con); return -1; } if (gBuf[0] != 0x2e) { pout("TapeAlerts Log Sense Failed\n"); PRINT_OFF(con); return -1; } pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3]; for (s=severities; *s; s++) { for (i = 4; i < pagelength; i += 5) { parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1]; 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(con); if (! failures) pout("TapeAlert: OK\n"); return failures; } static void scsiGetStartStopData(int device) { UINT32 u; int err, len, k, extra, pc; unsigned char * ucp; if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { PRINT_ON(con); pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err)); PRINT_OFF(con); return; } if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) { PRINT_ON(con); pout("StartStop Log Sense Failed, page mismatch\n"); PRINT_OFF(con); return; } len = ((gBuf[2] << 8) | gBuf[3]); ucp = gBuf + 4; for (k = len; k > 0; k -= extra, ucp += extra) { if (k < 3) { PRINT_ON(con); pout("StartStop Log Sense Failed: short\n"); PRINT_OFF(con); return; } extra = ucp[3] + 4; pc = (ucp[0] << 8) + ucp[1]; 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) { u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; if (0xffffffff != u) pout("Recommended maximum start stop count: %u times\n", u); } break; case 4: if (extra > 7) { u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; if (0xffffffff != u) pout("Current start stop count: %u times\n", u); } break; default: /* ignore */ break; } } } static void scsiPrintGrownDefectListLen(int device) { int err, dl_format, dl_len, div; memset(gBuf, 0, 4); if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, 4 /* bytes from index */, gBuf, 4))) { if (con->reportscsiioctl > 0) { PRINT_ON(con); pout("Read defect list (10) Failed: %s\n", scsiErrString(err)); PRINT_OFF(con); } return; } if (0x8 != (gBuf[1] & 0x18)) { PRINT_ON(con); pout("Read defect list: asked for grown list but didn't get it\n"); PRINT_OFF(con); return; } div = 0; dl_format = (gBuf[1] & 0x7); switch (dl_format) { case 0: /* short block */ div = 4; break; case 3: /* long block */ case 4: /* bytes from index */ case 5: /* physical sector */ div = 8; break; default: PRINT_ON(con); pout("defect list format %d unknown\n", dl_format); PRINT_OFF(con); break; } dl_len = (gBuf[2] << 8) + gBuf[3]; if (0 == dl_len) pout("Elements in grown defect list: 0\n"); else { if (0 == div) pout("Grown defect list length=%d bytes [unknown " "number of elements]\n", dl_len); else pout("Elements in grown defect list: %d\n", dl_len / div); } } static void scsiPrintSeagateCacheLPage(int device) { int k, j, num, pl, pc, err, len; unsigned char * ucp; unsigned char * xp; uint64_t ull; if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { PRINT_ON(con); pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err)); PRINT_OFF(con); return; } if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) { PRINT_ON(con); pout("Seagate Cache Log Sense Failed, page mismatch\n"); PRINT_OFF(con); return; } len = ((gBuf[2] << 8) | gBuf[3]) + 4; num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = (ucp[0] << 8) | ucp[1]; pl = ucp[3] + 4; switch (pc) { case 0: case 1: case 2: case 3: case 4: break; default: if (con->reportscsiioctl > 0) { PRINT_ON(con); pout("Vendor (Seagate) cache lpage has unexpected parameter" ", skip\n"); PRINT_OFF(con); } return; } num -= pl; ucp += pl; } pout("Vendor (Seagate) cache information\n"); num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = (ucp[0] << 8) | ucp[1]; 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; } k = pl - 4; xp = ucp + 4; if (k > (int)sizeof(ull)) { xp += (k - (int)sizeof(ull)); k = (int)sizeof(ull); } ull = 0; for (j = 0; j < k; ++j) { if (j > 0) ull <<= 8; ull |= xp[j]; } pout(" = %"PRIu64"\n", ull); num -= pl; ucp += pl; } } static void scsiPrintSeagateFactoryLPage(int device) { int k, j, num, pl, pc, len, err, good, bad; unsigned char * ucp; unsigned char * xp; uint64_t ull; if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { PRINT_ON(con); pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err)); PRINT_OFF(con); return; } if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) { PRINT_ON(con); pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n"); PRINT_OFF(con); return; } len = ((gBuf[2] << 8) | gBuf[3]) + 4; num = len - 4; ucp = &gBuf[0] + 4; good = 0; bad = 0; while (num > 3) { pc = (ucp[0] << 8) | ucp[1]; 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 (con->reportscsiioctl > 0) { PRINT_ON(con); pout("\nVendor (Seagate/Hitachi) factory lpage has too many " "unexpected parameters, skip\n"); PRINT_OFF(con); } return; } pout("Vendor (Seagate/Hitachi) factory information\n"); num = len - 4; ucp = &gBuf[0] + 4; while (num > 3) { pc = (ucp[0] << 8) | ucp[1]; 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 (con->reportscsiioctl > 0) { PRINT_ON(con); pout("Vendor (Seagate/Hitachi) factory lpage: " "unknown parameter code [0x%x]\n", pc); PRINT_OFF(con); } break; } if (good) { k = pl - 4; xp = ucp + 4; if (k > (int)sizeof(ull)) { xp += (k - (int)sizeof(ull)); k = (int)sizeof(ull); } ull = 0; for (j = 0; j < k; ++j) { if (j > 0) ull <<= 8; ull |= xp[j]; } if (0 == pc) pout(" = %.2f\n", uint64_to_double(ull) / 60.0 ); else pout(" = %"PRIu64"\n", ull); } num -= pl; ucp += pl; } } static void scsiPrintErrorCounterLog(int device) { struct scsiErrorCounter errCounterArr[3]; struct scsiErrorCounter * ecp; struct scsiNonMediumError nme; int found[3] = {0, 0, 0}; const char * pageNames[3] = {"read: ", "write: ", "verify: "}; int k; double processed_gb; 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 (k = 0; k < 7; ++k) { if (ecp->gotPC[k] && ecp->counter[k]) { found[2] = 1; break; } } } if (found[0] || found[1] || found[2]) { pout("\nError 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"); for (k = 0; k < 3; ++k) { if (! found[k]) continue; ecp = &errCounterArr[k]; pout("%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]); processed_gb = uint64_to_double(ecp->counter[5]) / 1000000000.0; pout(" %12.3f %8"PRIu64"\n", processed_gb, ecp->counter[6]); } } else pout("\nError Counter logging not supported\n"); if (gNonMediumELPage && (0 == scsiLogSense(device, NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { 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 (gLastNErrorLPage && (0 == scsiLogSense(device, LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { unsigned char * ucp; int num, k, pc, pl, truncated; num = (gBuf[2] << 8) + gBuf[3] + 4; truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; ucp = gBuf + 4; num -= 4; if (num < 4) pout("\nNo error events logged\n"); else { pout("\nLast n error events log page\n"); for (k = num; k > 0; k -= pl, ucp += pl) { if (k < 3) { pout(" <>\n"); break; } pl = ucp[3] + 4; pc = (ucp[0] << 8) + ucp[1]; if (pl > 4) { if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) { pout(" Error event %d:\n", pc); pout(" [binary]:\n"); dStrHex((const char *)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 (con->reportscsiioctl > 0) { pout(" Error event %d:\n", pc); pout(" [data counter??]:\n"); dStrHex((const char *)ucp + 4, pl - 4, 1); } } } } if (truncated) pout(" >>>> log truncated, fetched %d of %d available " "bytes\n", LOG_RESP_LONG_LEN, truncated); } } } 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 ", "Interrupted ('-X' switch)", "Interrupted (bus 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(int device) { int num, k, n, res, err, durationSec; int noheader = 1; int retval = 0; UINT8 * ucp; uint64_t ull=0; if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf, LOG_RESP_SELF_TEST_LEN, 0))) { PRINT_ON(con); pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err)); PRINT_OFF(con); return FAILSMART; } if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) { PRINT_ON(con); pout("Self-test Log Sense Failed, page mismatch\n"); PRINT_OFF(con); return FAILSMART; } // compute page length num = (gBuf[2] << 8) + gBuf[3]; // Log sense page length 0x190 bytes if (num != 0x190) { PRINT_ON(con); pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num); PRINT_OFF(con); return FAILSMART; } // loop through the twenty possible entries for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) { int i; // timestamp in power-on hours (or zero if test in progress) n = (ucp[6] << 8) | ucp[7]; // 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("\nSMART Self-test log\n"); 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", (ucp[0] << 8) | ucp[1], 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: 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 for (i = 0; i < 8; i++) { ull <<= 8; ull |= ucp[i+8]; } // print Address of First Failure, if sensible if ((~(uint64_t)0 != ull) && (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 self-tests have been logged\n"); else pout("\n"); if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, modese_len)) && (durationSec > 0)) { pout("Long (extended) Self Test duration: %d seconds " "[%.1f minutes]\n", durationSec, durationSec / 60.0); } 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", "halted until BM interval timer expires", /* 8 */ }; static const char * reassign_status[] = { "No reassignment needed", "Require Reassign or Write command", "Successfully reassigned", "Reserved [0x3]", "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(int device) { int num, j, m, err, pc, pl, truncated; int noheader = 1; int firstresult = 1; int retval = 0; UINT8 * ucp; if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { PRINT_ON(con); pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err)); PRINT_OFF(con); return FAILSMART; } if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) { PRINT_ON(con); pout("Background scan results Log Sense Failed, page mismatch\n"); PRINT_OFF(con); return FAILSMART; } // compute page length num = (gBuf[2] << 8) + gBuf[3] + 4; if (num < 20) { PRINT_ON(con); pout("Background scan results Log Sense length is %d, no scan " "status\n", num); PRINT_OFF(con); 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) { pc = (ucp[0] << 8) | ucp[1]; // pcb = ucp[2]; pl = ucp[3] + 4; switch (pc) { case 0: if (noheader) { noheader = 0; pout("\nBackground scan results log\n"); } 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 = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; pout(" Accumulated power on time, hours:minutes %d:%02d " "[%d minutes]\n", (j / 60), (j % 60), j); pout(" Number of background scans performed: %d, ", (ucp[10] << 8) + ucp[11]); pout("scan progress: %.2f%%\n", (double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0); break; default: if (noheader) { noheader = 0; pout("\nBackground scan results log\n"); } 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 = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; 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); return retval; } static const char * peripheral_dt_arr[] = { "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" }; static const char * transport_proto_arr[] = { "Fibre channel (FCP-2)", "Parallel SCSI (SPI-4)", "SSA", "IEEE 1394 (SBP-2)", "RDMA (SRP)", "iSCSI", "SAS", "ADT", "0x8", "0x9", "0xa", "0xb", "0xc", "0xd", "0xe", "0xf" }; /* Returns 0 on success, 1 on general error and 2 for early, clean exit */ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) { char manufacturer[9]; char product[17]; char revision[5]; char timedatetz[DATEANDEPOCHLEN]; struct scsi_iec_mode_page iec; int err, iec_err, len, req_len, avail_len, val; int is_tape = 0; int peri_dt = 0; int returnval=0; memset(gBuf, 0, 96); req_len = 36; if ((err = scsiStdInquiry(device, gBuf, req_len))) { PRINT_ON(con); pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err)); pout("Retrying with a 64 byte Standard Inquiry\n"); PRINT_OFF(con); /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ req_len = 64; if ((err = scsiStdInquiry(device, gBuf, req_len))) { PRINT_ON(con); pout("Standard Inquiry (64 bytes) failed [%s]\n", scsiErrString(err)); PRINT_OFF(con); return 1; } } avail_len = gBuf[4] + 5; len = (avail_len < req_len) ? avail_len : req_len; peri_dt = gBuf[0] & 0x1f; if (peripheral_type) *peripheral_type = peri_dt; if (len < 36) { PRINT_ON(con); pout("Short INQUIRY response, skip product id\n"); PRINT_OFF(con); return 1; } memset(manufacturer, 0, sizeof(manufacturer)); strncpy(manufacturer, (char *)&gBuf[8], 8); memset(product, 0, sizeof(product)); strncpy(product, (char *)&gBuf[16], 16); memset(revision, 0, sizeof(revision)); strncpy(revision, (char *)&gBuf[32], 4); if (all && (0 != strncmp(manufacturer, "ATA", 3))) pout("Device: %s %s Version: %s\n", manufacturer, product, revision); if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) { #if defined(_WIN32) || defined(__CYGWIN__) pout("please try changing device to /dev/hdX,N\n"); #else pout("please try adding '-d 3ware,N'\n"); pout("you may also need to change device to /dev/twaN or /dev/tweN\n"); #endif return 2; } else if ((len >= 42) && (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) { pout("please try '-d marvell'\n"); return 2; } else if ((0 == con->controller_explicit) && (0 == strncmp(manufacturer, "ATA ", 8)) && has_sat_pass_through(device, 0)) { con->controller_type = CONTROLLER_SAT; if (con->reportscsiioctl > 0) { PRINT_ON(con); pout("Detected SAT interface, switch to device type 'sat'\n"); PRINT_OFF(con); } return 2; } else if ((0 == con->controller_explicit) && (0 == strncmp(manufacturer, "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; /* 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(con); gIecMPage = 0; return 1; } } else modese_len = iec.modese_len; if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) { /* should use VPD page 0x83 and fall back to this page (0x80) * if 0x83 not supported. NAA requires a lot of decoding code */ len = gBuf[3]; gBuf[4 + len] = '\0'; pout("Serial number: %s\n", &gBuf[4]); } else if (con->reportscsiioctl > 0) { PRINT_ON(con); 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(con); } // print SCSI peripheral device type if (peri_dt < (int)(sizeof(peripheral_dt_arr) / sizeof(peripheral_dt_arr[0]))) pout("Device type: %s\n", peripheral_dt_arr[peri_dt]); else pout("Device type: <%d>\n", peri_dt); // See if transport protocol is known val = scsiFetchTransportProtocol(device, modese_len); if ((val >= 0) && (val <= 0xf)) pout("Transport protocol: %s\n", transport_proto_arr[val]); // print current time and date and timezone dateandtimezone(timedatetz); pout("Local Time is: %s\n", timedatetz); if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) || (SCSI_PT_MEDIUM_CHANGER == *peripheral_type)) is_tape = 1; // See if unit accepts SCSI commmands from us if ((err = scsiTestUnitReady(device))) { if (SIMPLE_ERR_NOT_READY == err) { PRINT_ON(con); 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(con); } else if (SIMPLE_ERR_NO_MEDIUM == err) { PRINT_ON(con); pout("NO MEDIUM present on device\n"); PRINT_OFF(con); } else if (SIMPLE_ERR_BECOMING_READY == err) { PRINT_ON(con); pout("device becoming ready (wait)\n"); PRINT_OFF(con); } else { PRINT_ON(con); pout("device Test Unit Ready [%s]\n", scsiErrString(err)); PRINT_OFF(con); } failuretest(MANDATORY_CMD, returnval|=FAILID); } if (iec_err) { if (!is_tape) { PRINT_ON(con); pout("Device does not support SMART"); if (con->reportscsiioctl > 0) pout(" [%s]\n", scsiErrString(iec_err)); else pout("\n"); PRINT_OFF(con); } gIecMPage = 0; return 0; } if (!is_tape) pout("Device supports SMART and 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(int device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { PRINT_ON(con); pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); PRINT_OFF(con); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) { PRINT_ON(con); pout("unable to enable Exception control and warning [%s]\n", scsiErrString(err)); PRINT_OFF(con); 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(int device) { struct scsi_iec_mode_page iec; int err; if ((err = scsiFetchIECmpage(device, &iec, modese_len))) { PRINT_ON(con); pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); PRINT_OFF(con); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) { PRINT_ON(con); pout("unable to disable Exception control and warning [%s]\n", scsiErrString(err)); PRINT_OFF(con); 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(int device) { UINT8 temp = 0; UINT8 trip = 0; if (scsiGetTemp(device, &temp, &trip)) return; if (temp) { if (255 != temp) pout("Current Drive Temperature: %d C\n", temp); else pout("Current Drive Temperature: \n"); } if (trip) pout("Drive Trip Temperature: %d C\n", trip); } /* Main entry point used by smartctl command. Return 0 for success */ int scsiPrintMain(int fd) { int checkedSupportedLogPages = 0; UINT8 peripheral_type = 0; int returnval = 0; int res, durationSec; res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo); if (res) { if (2 == res) return 0; else failuretest(MANDATORY_CMD, returnval |= FAILID); } if (con->smartenable) { if (scsiSmartEnable(fd)) failuretest(MANDATORY_CMD, returnval |= FAILSMART); } if (con->smartdisable) { if (scsiSmartDisable(fd)) failuretest(MANDATORY_CMD,returnval |= FAILSMART); } if (con->smartautosaveenable) { if (scsiSetControlGLTSD(fd, 0, modese_len)) { pout("Enable autosave (clear GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } } if (con->smartautosavedisable) { if (scsiSetControlGLTSD(fd, 1, modese_len)) { pout("Disable autosave (set GLTSD bit) failed\n"); failuretest(OPTIONAL_CMD,returnval |= FAILSMART); } } if (con->checksmart) { scsiGetSupportedLogPages(fd); checkedSupportedLogPages = 1; if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) || (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */ if (gTapeAlertsLPage) { if (con->driveinfo) pout("TapeAlert Supported\n"); if (-1 == scsiGetTapeAlertsData(fd, peripheral_type)) failuretest(OPTIONAL_CMD, returnval |= FAILSMART); } else pout("TapeAlert Not Supported\n"); } else { /* disk, cd/dvd, enclosure, etc */ if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) { if (-2 == res) returnval |= FAILSTATUS; else returnval |= FAILSMART; } } } if (con->smartvendorattrib) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(fd); if (gTempLPage) { if (con->checksmart) pout("\n"); scsiPrintTemp(fd); } if (gStartStopLPage) scsiGetStartStopData(fd); if (SCSI_PT_DIRECT_ACCESS == peripheral_type) { scsiPrintGrownDefectListLen(fd); if (gSeagateCacheLPage) scsiPrintSeagateCacheLPage(fd); if (gSeagateFactoryLPage) scsiPrintSeagateFactoryLPage(fd); } } if (con->smarterrorlog) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(fd); scsiPrintErrorCounterLog(fd); if (1 == scsiFetchControlGLTSD(fd, modese_len, 1)) pout("\n[GLTSD (Global Logging Target Save Disable) set. " "Enable Save with '-S on']\n"); } if (con->smartselftestlog) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(fd); res = 0; if (gSelfTestLPage) res = scsiPrintSelfTest(fd); else { pout("Device does not support Self Test logging\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); } if (con->smartbackgroundlog) { if (! checkedSupportedLogPages) scsiGetSupportedLogPages(fd); res = 0; if (gBackgroundResultsLPage) res = scsiPrintBackgroundResults(fd); else { pout("Device does not support Background scan results logging\n"); failuretest(OPTIONAL_CMD, returnval|=FAILSMART); } if (0 != res) failuretest(OPTIONAL_CMD, returnval|=res); } if (con->smartexeoffimmediate) { if (scsiSmartDefaultSelfTest(fd)) return returnval | FAILSMART; pout("Default Self Test Successful\n"); } if (con->smartshortcapselftest) { if (scsiSmartShortCapSelfTest(fd)) return returnval | FAILSMART; pout("Short Foreground Self Test Successful\n"); } if (con->smartshortselftest ) { if (scsiSmartShortSelfTest(fd)) return returnval | FAILSMART; pout("Short Background Self Test has begun\n"); pout("Use smartctl -X to abort test\n"); } if (con->smartextendselftest) { if (scsiSmartExtendSelfTest(fd)) return returnval | FAILSMART; pout("Extended Background Self Test has begun\n"); if ((0 == scsiFetchExtendedSelfTestTime(fd, &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"); } if (con->smartextendcapselftest) { if (scsiSmartExtendCapSelfTest(fd)) return returnval | FAILSMART; pout("Extended Foreground Self Test Successful\n"); } if (con->smartselftestabort) { if (scsiSmartSelfTestAbort(fd)) return returnval | FAILSMART; pout("Self Test returned without error\n"); } return returnval; }