X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=scsiprint.cpp;h=47f5a3692bf8338f617c1c136854e975982e2f27;hb=7af9336546f5d8f23d8760b67b3d835773e52f04;hp=c1e9743ddba3f2ea7e73e334499958f1415aee18;hpb=1e0b95bd15307f123dc9b2587df3f708fdfc11d8;p=mirror_smartmontools-debian.git diff --git a/scsiprint.cpp b/scsiprint.cpp index c1e9743..47f5a36 100644 --- a/scsiprint.cpp +++ b/scsiprint.cpp @@ -1,13 +1,11 @@ /* * scsiprint.cpp * - * Home page of code is: http://smartmontools.sourceforge.net + * Home page of code is: http://www.smartmontools.org * - * Copyright (C) 2002-8 Bruce Allen + * Copyright (C) 2002-11 Bruce Allen * Copyright (C) 2000 Michael Cornwell - * - * Additional SCSI work: - * Copyright (C) 2003-8 Douglas Gilbert + * Copyright (C) 2003-15 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 @@ -15,8 +13,8 @@ * any later version. * * You should have received a copy of the GNU General Public License - * (for example COPYING); if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * (for example COPYING); if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This code was originally developed as a Senior Thesis by Michael Cornwell * at the Concurrent Systems Laboratory (now part of the Storage Systems @@ -33,27 +31,22 @@ #include "config.h" #include "int64.h" -#include "extern.h" #include "scsicmds.h" +#include "atacmds.h" // smart_command_set +#include "dev_interface.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.121 2008/03/04 22:09:47 ballen4705 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; +const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4189 2015-12-16 14:53:41Z dpgilbert $" + SCSIPRINT_H_CVSID; -// 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_LONG_LEN ((62 * 256) + 252) #define LOG_RESP_TAPE_ALERT_LEN 0x144 /* Log pages supported */ @@ -67,7 +60,11 @@ static int gVerifyECounterLPage = 0; static int gNonMediumELPage = 0; static int gLastNErrorLPage = 0; static int gBackgroundResultsLPage = 0; +static int gProtocolSpecificLPage = 0; static int gTapeAlertsLPage = 0; +static int gSSMediaLPage = 0; + +/* Vendor specific log pages */ static int gSeagateCacheLPage = 0; static int gSeagateFactoryLPage = 0; @@ -77,21 +74,19 @@ 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) +static void +scsiGetSupportedLogPages(scsi_device * 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)); + if (scsi_debugmode > 0) + pout("Log Sense for supported pages failed [%s]\n", + scsiErrString(err)); return; - } + } for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) { switch (gBuf[i]) @@ -126,9 +121,15 @@ static void scsiGetSupportedLogPages(int device) case BACKGROUND_RESULTS_LPAGE: gBackgroundResultsLPage = 1; break; + case PROTOCOL_SPECIFIC_LPAGE: + gProtocolSpecificLPage = 1; + break; case TAPE_ALERTS_LPAGE: gTapeAlertsLPage = 1; break; + case SS_MEDIA_LPAGE: + gSSMediaLPage = 1; + break; case SEAGATE_CACHE_LPAGE: gSeagateCacheLPage = 1; break; @@ -143,7 +144,8 @@ static void scsiGetSupportedLogPages(int device) /* 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) +static int +scsiGetSmartData(scsi_device * device, bool attribs) { UINT8 asc; UINT8 ascq; @@ -151,27 +153,24 @@ static int scsiGetSmartData(int device, int attribs) UINT8 triptemp = 0; const char * cp; int err = 0; - - PRINT_ON(con); + print_on(); if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq, ¤ttemp, &triptemp)) { /* error message already announced */ - PRINT_OFF(con); + print_off(); return -1; } - PRINT_OFF(con); + print_off(); 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); + print_on(); + pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); + print_off(); } 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); @@ -181,33 +180,35 @@ static int scsiGetSmartData(int device, int attribs) if (triptemp) pout("Drive Trip Temperature: %d C\n", triptemp); } + pout("\n"); return err; } // Returns number of logged errors or zero if none or -1 if fetching // TapeAlerts fails -static char *severities = "CWI"; +static const char * const severities = "CWI"; -static int scsiGetTapeAlertsData(int device, int peripheral_type) +static int +scsiGetTapeAlertsData(scsi_device * device, int peripheral_type) { unsigned short pagelength; unsigned short parametercode; int i, err; - char *s; + const char *s; const char *ts; int failures = 0; - PRINT_ON(con); + print_on(); 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); + print_off(); return -1; } if (gBuf[0] != 0x2e) { pout("TapeAlerts Log Sense Failed\n"); - PRINT_OFF(con); + print_off(); return -1; } pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3]; @@ -222,14 +223,15 @@ static int scsiGetTapeAlertsData(int device, int peripheral_type) scsiTapeAlertsTapeDevice(parametercode); if (*ts == *s) { if (!failures) - pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n"); + pout("TapeAlert Errors (C=Critical, W=Warning, " + "I=Informational):\n"); pout("[0x%02x] %s\n", parametercode, ts); - failures += 1; + failures += 1; } } } } - PRINT_OFF(con); + print_off(); if (! failures) pout("TapeAlert: OK\n"); @@ -237,36 +239,37 @@ static int scsiGetTapeAlertsData(int device, int peripheral_type) return failures; } -static void scsiGetStartStopData(int device) +static void +scsiGetStartStopData(scsi_device * device) { - UINT32 u; - int err, len, k, extra, pc; + int err, len, k, extra; unsigned char * ucp; if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { - PRINT_ON(con); + print_on(); pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); return; } if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) { - PRINT_ON(con); + print_on(); pout("StartStop Log Sense Failed, page mismatch\n"); - PRINT_OFF(con); + print_off(); 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); + print_on(); pout("StartStop Log Sense Failed: short\n"); - PRINT_OFF(con); + print_off(); return; } extra = ucp[3] + 4; - pc = (ucp[0] << 8) + ucp[1]; + int pc = (ucp[0] << 8) + ucp[1]; + UINT32 u; switch (pc) { case 1: if (10 == extra) @@ -280,7 +283,7 @@ static void scsiGetStartStopData(int device) 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", + pout("Specified cycle count over device lifetime: %u\n", u); } break; @@ -288,7 +291,22 @@ static void scsiGetStartStopData(int device) 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); + pout("Accumulated start-stop cycles: %u\n", u); + } + break; + case 5: + if (extra > 7) { + u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; + if (0xffffffff != u) + pout("Specified load-unload count over device " + "lifetime: %u\n", u); + } + break; + case 6: + if (extra > 7) { + u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; + if (0xffffffff != u) + pout("Accumulated load-unload cycles: %u\n", u); } break; default: @@ -296,26 +314,57 @@ static void scsiGetStartStopData(int device) break; } } -} +} -static void scsiPrintGrownDefectListLen(int device) +static void +scsiPrintGrownDefectListLen(scsi_device * 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); + int err, dl_format, got_rd12; + unsigned int dl_len, div; + + 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 */ + if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */, + 4 /* format: bytes from index */, gBuf, 4))) { + if (scsi_debugmode > 0) { + print_on(); + pout("Read defect list (10) Failed: %s\n", 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("Read defect list (12) Failed: %s\n", scsiErrString(err)); + print_off(); + } + return; } - return; + } else + got_rd12 = 1; + + if (got_rd12) { + int generation = (gBuf[2] << 8) + gBuf[3]; + if ((generation > 1) && (scsi_debugmode > 0)) { + print_on(); + pout("Read defect list (12): generation=%d\n", generation); + print_off(); + } + dl_len = (gBuf[4] << 24) + (gBuf[5] << 16) + (gBuf[6] << 8) + gBuf[7]; + } else { + dl_len = (gBuf[2] << 8) + gBuf[3]; } if (0x8 != (gBuf[1] & 0x18)) { - PRINT_ON(con); + print_on(); pout("Read defect list: asked for grown list but didn't get it\n"); - PRINT_OFF(con); + print_off(); return; } div = 0; @@ -324,47 +373,51 @@ static void scsiPrintGrownDefectListLen(int device) 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(con); + print_on(); pout("defect list format %d unknown\n", dl_format); - PRINT_OFF(con); + print_off(); break; } - dl_len = (gBuf[2] << 8) + gBuf[3]; if (0 == dl_len) - pout("Elements in grown defect list: 0\n"); + pout("Elements in grown defect list: 0\n\n"); else { if (0 == div) - pout("Grown defect list length=%d bytes [unknown " - "number of elements]\n", dl_len); + pout("Grown defect list length=%u bytes [unknown " + "number of elements]\n\n", dl_len); else - pout("Elements in grown defect list: %d\n", dl_len / div); + pout("Elements in grown defect list: %u\n\n", dl_len / div); } } -static void scsiPrintSeagateCacheLPage(int device) +static void +scsiPrintSeagateCacheLPage(scsi_device * device) { - int k, j, num, pl, pc, err, len; + int 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); + print_on(); pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); return; } if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) { - PRINT_ON(con); + print_on(); pout("Seagate Cache Log Sense Failed, page mismatch\n"); - PRINT_OFF(con); + print_off(); return; } len = ((gBuf[2] << 8) | gBuf[3]) + 4; @@ -376,12 +429,12 @@ static void scsiPrintSeagateCacheLPage(int device) switch (pc) { case 0: case 1: case 2: case 3: case 4: break; - default: - if (con->reportscsiioctl > 0) { - PRINT_ON(con); + default: + if (scsi_debugmode > 0) { + print_on(); pout("Vendor (Seagate) cache lpage has unexpected parameter" ", skip\n"); - PRINT_OFF(con); + print_off(); } return; } @@ -404,42 +457,43 @@ static void scsiPrintSeagateCacheLPage(int device) "> segment size"); break; default: pout(" Unknown Seagate parameter code [0x%x]", pc); break; } - k = pl - 4; - xp = ucp + 4; + int k = pl - 4; + unsigned char * 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) { + for (int j = 0; j < k; ++j) { if (j > 0) ull <<= 8; ull |= xp[j]; } - pout(" = %"PRIu64"\n", ull); + pout(" = %" PRIu64 "\n", ull); num -= pl; ucp += pl; } + pout("\n"); } -static void scsiPrintSeagateFactoryLPage(int device) +static void +scsiPrintSeagateFactoryLPage(scsi_device * device) { - int k, j, num, pl, pc, len, err, good, bad; + int 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); + print_on(); pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); return; } if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) { - PRINT_ON(con); + print_on(); pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n"); - PRINT_OFF(con); + print_off(); return; } len = ((gBuf[2] << 8) | gBuf[3]) + 4; @@ -454,7 +508,7 @@ static void scsiPrintSeagateFactoryLPage(int device) case 0: case 8: ++good; break; - default: + default: ++bad; break; } @@ -462,11 +516,11 @@ static void scsiPrintSeagateFactoryLPage(int device) ucp += pl; } if ((good < 2) || (bad > 4)) { /* heuristic */ - if (con->reportscsiioctl > 0) { - PRINT_ON(con); + if (scsi_debugmode > 0) { + print_on(); pout("\nVendor (Seagate/Hitachi) factory lpage has too many " "unexpected parameters, skip\n"); - PRINT_OFF(con); + print_off(); } return; } @@ -485,46 +539,44 @@ static void scsiPrintSeagateFactoryLPage(int device) good = 1; break; default: - if (con->reportscsiioctl > 0) { - PRINT_ON(con); + if (scsi_debugmode > 0) { + print_on(); pout("Vendor (Seagate/Hitachi) factory lpage: " "unknown parameter code [0x%x]\n", pc); - PRINT_OFF(con); + print_off(); } break; } if (good) { - k = pl - 4; - xp = ucp + 4; + int k = pl - 4; + unsigned char * 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) { + for (int 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 ); + pout(" = %.2f\n", ull / 60.0 ); else - pout(" = %"PRIu64"\n", ull); + pout(" = %" PRIu64 "\n", ull); } num -= pl; ucp += pl; } + pout("\n"); } -static void scsiPrintErrorCounterLog(int device) +static void +scsiPrintErrorCounterLog(scsi_device * 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))) { @@ -540,7 +592,7 @@ static void scsiPrintErrorCounterLog(int device) VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]); ecp = &errCounterArr[2]; - for (k = 0; k < 7; ++k) { + for (int k = 0; k < 7; ++k) { if (ecp->gotPC[k] && ecp->counter[k]) { found[2] = 1; break; @@ -548,60 +600,59 @@ static void scsiPrintErrorCounterLog(int device) } } if (found[0] || found[1] || found[2]) { - pout("\nError counter log:\n"); + 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"); - for (k = 0; k < 3; ++k) { + for (int 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], + static const char * const pageNames[3] = {"read: ", "write: ", "verify: "}; + 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]); + double processed_gb = ecp->counter[5] / 1000000000.0; + pout(" %12.3f %8" PRIu64 "\n", processed_gb, ecp->counter[6]); } } - else - pout("\nError Counter logging not supported\n"); + 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); + pout("\nNon-medium error count: %8" PRIu64 "\n", nme.counterPC0); if (nme.gotTFE_H) - pout("Track following error count [Hitachi]: %8"PRIu64"\n", + pout("Track following error count [Hitachi]: %8" PRIu64 "\n", nme.counterTFE_H); if (nme.gotPE_H) - pout("Positioning error count [Hitachi]: %8"PRIu64"\n", + 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; + int num = (gBuf[2] << 8) + gBuf[3] + 4; + int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; if (truncated) num = LOG_RESP_LONG_LEN; - ucp = gBuf + 4; + 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 (k = num; k > 0; k -= pl, ucp += pl) { + for (int k = num, pl; k > 0; k -= pl, ucp += pl) { if (k < 3) { pout(" <>\n"); break; } pl = ucp[3] + 4; - pc = (ucp[0] << 8) + ucp[1]; + int pc = (ucp[0] << 8) + ucp[1]; if (pl > 4) { if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) { pout(" Error event %d:\n", pc); @@ -611,7 +662,7 @@ static void scsiPrintErrorCounterLog(int device) pout(" Error event %d:\n", pc); pout(" %.*s\n", pl - 4, (const char *)(ucp + 4)); } else { - if (con->reportscsiioctl > 0) { + if (scsi_debugmode > 0) { pout(" Error event %d:\n", pc); pout(" [data counter??]:\n"); dStrHex((const char *)ucp + 4, pl - 4, 1); @@ -624,34 +675,35 @@ static void scsiPrintErrorCounterLog(int device) "bytes\n", LOG_RESP_LONG_LEN, truncated); } } + pout("\n"); } static const char * self_test_code[] = { - "Default ", - "Background short", - "Background long ", + "Default ", + "Background short", + "Background long ", "Reserved(3) ", - "Abort background", - "Foreground short", + "Abort background", + "Foreground short", "Foreground long ", "Reserved(7) " }; static const char * self_test_result[] = { "Completed ", - "Interrupted ('-X' switch)", - "Interrupted (bus reset ?)", + "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(8) ", + "Reserved(9) ", + "Reserved(10) ", + "Reserved(11) ", + "Reserved(12) ", + "Reserved(13) ", "Reserved(14) ", "Self test in progress ..." }; @@ -660,34 +712,44 @@ static const char * self_test_result[] = { // 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) +static int +scsiPrintSelfTest(scsi_device * device) { - int num, k, n, res, err, durationSec; + int num, k, err, durationSec; int noheader = 1; int retval = 0; UINT8 * ucp; uint64_t ull=0; + struct scsi_sense_disect sense_info; + + // check if test is running + if (!scsiRequestSense(device, &sense_info) && + (sense_info.asc == 0x04 && sense_info.ascq == 0x09 && + sense_info.progress != -1)) { + pout("Self-test execution status:\t\t%d%% of test remaining\n", + 100 - ((sense_info.progress * 100) / 65535)); + } if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf, LOG_RESP_SELF_TEST_LEN, 0))) { - PRINT_ON(con); + print_on(); pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) { - PRINT_ON(con); + print_on(); pout("Self-test Log Sense Failed, page mismatch\n"); - PRINT_OFF(con); + print_off(); return FAILSMART; } // compute page length num = (gBuf[2] << 8) + gBuf[3]; // Log sense page length 0x190 bytes if (num != 0x190) { - PRINT_ON(con); + print_on(); pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num); - PRINT_OFF(con); + print_off(); return FAILSMART; } // loop through the twenty possible entries @@ -695,7 +757,7 @@ static int scsiPrintSelfTest(int device) int i; // timestamp in power-on hours (or zero if test in progress) - n = (ucp[6] << 8) | ucp[7]; + int 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. @@ -704,7 +766,7 @@ static int scsiPrintSelfTest(int device) // only print header if needed if (noheader) { - pout("\nSMART Self-test log\n"); + pout("SMART Self-test log\n"); pout("Num Test Status segment " "LifeTime LBA_first_err [SK ASC ASQ]\n"); pout(" Description number " @@ -713,11 +775,12 @@ static int scsiPrintSelfTest(int device) } // print parameter code (test number) & self-test code text - pout("#%2d %s", (ucp[0] << 8) | ucp[1], + 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: + int res; switch ((res = ucp[4] & 0xf)) { case 0x3: // an unknown error occurred while the device server @@ -765,9 +828,9 @@ static int scsiPrintSelfTest(int device) if (n==0 && res==0xf) // self-test in progress pout(" NOW"); - else + else pout(" %5d", n); - + // construct 8-byte integer address of first failure for (i = 0; i < 8; i++) { ull <<= 8; @@ -778,8 +841,8 @@ static int scsiPrintSelfTest(int device) 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); + snprintf(buff, sizeof(buff), "%" PRIu64, ull); + // snprintf(buff, sizeof(buff), "0x%" PRIx64, ull); pout("%18s", buff); } else pout(" -"); @@ -796,12 +859,12 @@ static int scsiPrintSelfTest(int device) if (noheader) pout("No self-tests have been logged\n"); else - pout("\n"); - if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, + if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, modese_len)) && (durationSec > 0)) { - pout("Long (extended) Self Test duration: %d seconds " + pout("\nLong (extended) Self Test duration: %d seconds " "[%.1f minutes]\n", durationSec, durationSec / 60.0); } + pout("\n"); return retval; } @@ -814,15 +877,15 @@ static const char * bms_status[] = { "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 */ + "waiting until BMS interval timer expires", /* 8 */ }; static const char * reassign_status[] = { - "No reassignment needed", - "Require Reassign or Write command", + "Reserved [0x0]", + "Require Write or Reassign Blocks command", "Successfully reassigned", "Reserved [0x3]", - "Failed", + "Reassignment by disk failed", "Recovered via rewrite in-place", "Reassigned by app, has valid data", "Reassigned by app, has no valid data", @@ -833,9 +896,10 @@ static const char * reassign_status[] = { // 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) +static int +scsiPrintBackgroundResults(scsi_device * device) { - int num, j, m, err, pc, pl, truncated; + int num, j, m, err, truncated; int noheader = 1; int firstresult = 1; int retval = 0; @@ -843,24 +907,24 @@ static int scsiPrintBackgroundResults(int device) if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { - PRINT_ON(con); + print_on(); pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); return FAILSMART; } if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) { - PRINT_ON(con); + print_on(); pout("Background scan results Log Sense Failed, page mismatch\n"); - PRINT_OFF(con); + print_off(); return FAILSMART; } // compute page length num = (gBuf[2] << 8) + gBuf[3] + 4; if (num < 20) { - PRINT_ON(con); + print_on(); pout("Background scan results Log Sense length is %d, no scan " "status\n", num); - PRINT_OFF(con); + print_off(); return FAILSMART; } truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; @@ -869,14 +933,14 @@ static int scsiPrintBackgroundResults(int device) ucp = gBuf + 4; num -= 4; while (num > 3) { - pc = (ucp[0] << 8) | ucp[1]; + int pc = (ucp[0] << 8) | ucp[1]; // pcb = ucp[2]; - pl = ucp[3] + 4; + int pl = ucp[3] + 4; switch (pc) { case 0: if (noheader) { noheader = 0; - pout("\nBackground scan results log\n"); + pout("Background scan results log\n"); } pout(" Status: "); if ((pl < 16) || (num < 16)) { @@ -895,6 +959,8 @@ static int scsiPrintBackgroundResults(int device) (ucp[10] << 8) + ucp[11]); pout("scan progress: %.2f%%\n", (double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0); + pout(" Number of background medium scans performed: %d\n", + (ucp[14] << 8) + ucp[15]); break; default: if (noheader) { @@ -931,9 +997,399 @@ static int scsiPrintBackgroundResults(int device) 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 * ucp; + + if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf, + LOG_RESP_LONG_LEN, 0))) { + print_on(); + pout("scsiPrintSSMedia Failed [%s]\n", scsiErrString(err)); + print_off(); + return FAILSMART; + } + if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) { + print_on(); + pout("Solid state media Log Sense Failed, page mismatch\n"); + print_off(); + return FAILSMART; + } + // compute page length + num = (gBuf[2] << 8) + gBuf[3] + 4; + if (num < 12) { + print_on(); + pout("Solid state media Log Sense length is %d, too short\n", 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 = (ucp[0] << 8) | ucp[1]; + // pcb = ucp[2]; + int pl = ucp[3] + 4; + switch (pc) { + case 1: + if (pl < 8) { + print_on(); + pout("SS Media Percentage used endurance indicator parameter " + "too short (pl=%d)\n", pl); + print_off(); + return FAILSMART; + } + pout("Percentage used endurance indicator: %d%%\n", ucp[7]); + default: /* ignore other parameter codes */ + break; + } + 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 (WATING 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, n, nphys, t, sz, spld_len; + unsigned char * vcp; + uint64_t ull; + char s[64]; + + sz = sizeof(s); + // pcb = ucp[2]; + t = (ucp[0] << 8) | ucp[1]; + 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)); + for (n = 0, ull = vcp[8]; n < 8; ++n) { + ull <<= 8; ull |= vcp[8 + n]; + } + pout(" SAS address = 0x%" PRIx64 "\n", ull); + for (n = 0, ull = vcp[16]; n < 8; ++n) { + ull <<= 8; ull |= vcp[16 + n]; + } + pout(" attached SAS address = 0x%" PRIx64 "\n", ull); + pout(" attached phy identifier = %d\n", vcp[24]); + unsigned int ui; + ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35]; + pout(" Invalid DWORD count = %u\n", ui); + ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39]; + pout(" Running disparity error count = %u\n", ui); + ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43]; + pout(" Loss of DWORD synchronization = %u\n", ui); + ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47]; + 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 = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) | + xcp[7]; + pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) | + xcp[11]; + 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 (6 != (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; + + if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf, + LOG_RESP_LONG_LEN, 0))) { + print_on(); + pout("scsiPrintSasPhy Log Sense Failed [%s]\n\n", scsiErrString(err)); + print_off(); + return FAILSMART; + } + if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) { + print_on(); + pout("Protocol specific Log Sense Failed, page mismatch\n\n"); + print_off(); + return FAILSMART; + } + // compute page length + num = (gBuf[2] << 8) + gBuf[3]; + if (1 != show_protocol_specific_page(gBuf, num + 4)) { + print_on(); + pout("Only support protocol specific log page on SAS devices\n\n"); + 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("scsiPrintSasPhy Log Select (reset) Failed [%s]\n\n", + scsiErrString(err)); + print_off(); + return FAILSMART; + } + } + return 0; +} + + static const char * peripheral_dt_arr[] = { "disk", "tape", @@ -951,6 +1407,22 @@ static const char * peripheral_dt_arr[] = { "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" }; static const char * transport_proto_arr[] = { @@ -960,11 +1432,11 @@ static const char * transport_proto_arr[] = { "IEEE 1394 (SBP-2)", "RDMA (SRP)", "iSCSI", - "SAS", + "SAS (SPL-3)", "ADT", - "0x8", - "0x9", - "0xa", + "ATA (ACS-2)", + "UAS", + "SOP", "0xb", "0xc", "0xd", @@ -973,82 +1445,73 @@ static const char * transport_proto_arr[] = { }; /* Returns 0 on success, 1 on general error and 2 for early, clean exit */ -static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) +static int +scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool 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 err, iec_err, len, req_len, avail_len, scsi_version; + bool is_tape = false; int peri_dt = 0; - int returnval=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(con); + print_on(); pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err)); pout("Retrying with a 64 byte Standard Inquiry\n"); - PRINT_OFF(con); + print_off(); /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */ req_len = 64; if ((err = scsiStdInquiry(device, gBuf, req_len))) { - PRINT_ON(con); + print_on(); pout("Standard Inquiry (64 bytes) failed [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); 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; + *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(con); + print_on(); pout("Short INQUIRY response, skip product id\n"); - PRINT_OFF(con); + print_off(); 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))) { + // 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 vendor[8+1], product[16+1], revision[4+1]; + scsi_format_id_string(vendor, (const unsigned char *)&gBuf[8], 8); + scsi_format_id_string(product, (const unsigned char *)&gBuf[16], 16); + scsi_format_id_string(revision, (const unsigned char *)&gBuf[32], 4); + + pout("=== START OF INFORMATION SECTION ===\n"); + pout("Vendor: %.8s\n", vendor); + pout("Product: %.16s\n", product); + if (gBuf[32] >= ' ') + pout("Revision: %.4s\n", revision); + if (scsi_version == 0x6) + pout("Compliance: SPC-4\n"); + else if (scsi_version == 0x7) + pout("Compliance: SPC-5\n"); + } + + 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; @@ -1056,128 +1519,268 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) 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) */ + unsigned int lb_size = 0; + unsigned char lb_prov_resp[8]; + char lb_str[16]; + int lb_per_pb_exp = 0; + uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp); + + 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); + pout("User Capacity: %s bytes [%s]\n", cap_str, si_str); + snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size); + pout("Logical block size: %s bytes\n", lb_str); + } + int lbpme = -1; + int lbprz = -1; + if (protect || lb_per_pb_exp) { + unsigned char rc16_12[20] = {0, }; + + if (0 == scsiGetProtPBInfo(device, rc16_12)) { + lb_per_pb_exp = rc16_12[1] & 0xf; /* just in case */ + if (lb_per_pb_exp > 0) { + snprintf(lb_str, sizeof(lb_str) - 1, "%u", + (lb_size * (1 << lb_per_pb_exp))); + pout("Physical block size: %s bytes\n", lb_str); + int n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3]; + if (n > 0) // not common so cut the clutter + pout("Lowest aligned LBA: %d\n", n); + } + if (rc16_12[0] & 0x1) { /* PROT_EN set */ + int p_type = ((rc16_12[0] >> 1) & 0x7); + + switch (p_type) { + case 0 : + pout("Formatted with type 1 protection\n"); + break; + case 1 : + pout("Formatted with type 2 protection\n"); + break; + case 2 : + pout("Formatted with type 3 protection\n"); + break; + default: + pout("Formatted with unknown protection type [%d]\n", + p_type); + break; + } + int p_i_exp = ((rc16_12[1] >> 4) & 0xf); + + if (p_i_exp > 0) + pout("%d protection information intervals per " + "logical block\n", (1 << p_i_exp)); + } + /* Pick up some LB provisioning info since its available */ + lbpme = !! (rc16_12[2] & 0x80); + lbprz = !! (rc16_12[2] & 0x40); + } + } + if (0 == scsiInquiryVpd(device, SCSI_VPD_LOGICAL_BLOCK_PROVISIONING, + lb_prov_resp, sizeof(lb_prov_resp))) { + int prov_type = lb_prov_resp[6] & 0x7; + + if (-1 == lbprz) + lbprz = !! (lb_prov_resp[5] & 0x4); + switch (prov_type) { + case 0: + pout("LB provisioning type: unreported, LBPME=%d, LBPRZ=%d\n", + lbpme, 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) + 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) + pout("Rotation Rate: Solid State Device\n"); + else if ((rpm <= 0x400) || (0xffff == rpm)) + ; // Reserved + else + pout("Rotation Rate: %d rpm\n", 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; + } + if (cp) + pout("Form Factor: %s inches\n", 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(con); + print_off(); gIecMPage = 0; return 1; } } else modese_len = iec.modese_len; - if (!con->dont_print_serial) { - 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 */ + if (! dont_print_serial_number) { + if (0 == (err = scsiInquiryVpd(device, SCSI_VPD_DEVICE_IDENTIFICATION, + gBuf, 252))) { + char s[256]; + len = gBuf[3]; - gBuf[4 + len] = '\0'; - pout("Serial number: %s\n", &gBuf[4]); + 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(); } - else if (con->reportscsiioctl > 0) { - PRINT_ON(con); + 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); + pout("Serial number: %s\n", 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(con); + print_off(); } } // print SCSI peripheral device type - if (peri_dt < (int)(sizeof(peripheral_dt_arr) / + if (peri_dt < (int)(sizeof(peripheral_dt_arr) / sizeof(peripheral_dt_arr[0]))) - pout("Device type: %s\n", peripheral_dt_arr[peri_dt]); + pout("Device type: %s\n", peripheral_dt_arr[peri_dt]); else - pout("Device type: <%d>\n", peri_dt); + 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]); + 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 dateandtimezone(timedatetz); - pout("Local Time is: %s\n", 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); + 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(con); + print_off(); } else if (SIMPLE_ERR_NO_MEDIUM == err) { - PRINT_ON(con); + print_on(); pout("NO MEDIUM present on device\n"); - PRINT_OFF(con); + print_off(); } else if (SIMPLE_ERR_BECOMING_READY == err) { - PRINT_ON(con); + print_on(); pout("device becoming ready (wait)\n"); - PRINT_OFF(con); + print_off(); } else { - PRINT_ON(con); + print_on(); pout("device Test Unit Ready [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); } + int returnval = 0; // TODO: exit with FAILID if failuretest returns failuretest(MANDATORY_CMD, returnval|=FAILID); } - + if (iec_err) { if (!is_tape) { - PRINT_ON(con); - pout("Device does not support SMART"); - if (con->reportscsiioctl > 0) + print_on(); + pout("SMART support is: Unavailable - device lacks SMART capability.\n"); + if (scsi_debugmode > 0) pout(" [%s]\n", scsiErrString(iec_err)); - else - pout("\n"); - PRINT_OFF(con); + print_off(); } gIecMPage = 0; return 0; } if (!is_tape) - pout("Device supports SMART and is %s\n", + 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"); + pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? + "Temperature Warning: Enabled" : + "Temperature Warning: Disabled or Not Supported"); return 0; } -static int scsiSmartEnable(int device) +static int +scsiSmartEnable(scsi_device * 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", + print_on(); + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) { - PRINT_ON(con); + print_on(); pout("unable to enable Exception control and warning [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + 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", + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); return 1; } else @@ -1189,31 +1792,32 @@ static int scsiSmartEnable(int device) scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled"); return 0; } - -static int scsiSmartDisable(int device) + +static int +scsiSmartDisable(scsi_device * 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", + print_on(); + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + print_off(); return 1; } else modese_len = iec.modese_len; if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) { - PRINT_ON(con); + print_on(); pout("unable to disable Exception control and warning [%s]\n", scsiErrString(err)); - PRINT_OFF(con); + 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", + pout("unable to fetch IEC (SMART) mode page [%s]\n", scsiErrString(err)); return 1; } else @@ -1226,14 +1830,15 @@ static int scsiSmartDisable(int device) return 0; } -static void scsiPrintTemp(int device) +static void +scsiPrintTemp(scsi_device * 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); @@ -1242,162 +1847,293 @@ static void scsiPrintTemp(int device) } if (trip) pout("Drive Trip Temperature: %d C\n", trip); + if (temp || trip) + pout("\n"); } /* Main entry point used by smartctl command. Return 0 for success */ -int scsiPrintMain(int fd) +int +scsiPrintMain(scsi_device * device, const scsi_print_options & options) { int checkedSupportedLogPages = 0; UINT8 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; - res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo); + 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); + 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 (con->smartenable) { - if (scsiSmartEnable(fd)) + if (options.smart_enable) { + if (scsiSmartEnable(device)) failuretest(MANDATORY_CMD, returnval |= FAILSMART); + any_output = true; } - if (con->smartdisable) { - if (scsiSmartDisable(fd)) + if (options.smart_disable) { + if (scsiSmartDisable(device)) failuretest(MANDATORY_CMD,returnval |= FAILSMART); + any_output = true; } - - 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); + + 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 ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) || - (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */ + if (is_tape) { if (gTapeAlertsLPage) { - if (con->driveinfo) + if (options.drive_info) pout("TapeAlert Supported\n"); - if (-1 == scsiGetTapeAlertsData(fd, peripheral_type)) + 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(fd, con->smartvendorattrib))) { + if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) { if (-2 == res) returnval |= FAILSTATUS; else returnval |= FAILSMART; } } - } - if (con->smartvendorattrib) { + any_output = true; + } + + if (is_disk && options.smart_ss_media_log) { if (! checkedSupportedLogPages) - scsiGetSupportedLogPages(fd); - if (gTempLPage) { - if (con->checksmart) - pout("\n"); - scsiPrintTemp(fd); - } + scsiGetSupportedLogPages(device); + res = 0; + if (gSSMediaLPage) + res = scsiPrintSSMedia(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(fd); - if (SCSI_PT_DIRECT_ACCESS == peripheral_type) { - scsiPrintGrownDefectListLen(fd); + scsiGetStartStopData(device); + if (is_disk) { + scsiPrintGrownDefectListLen(device); if (gSeagateCacheLPage) - scsiPrintSeagateCacheLPage(fd); + scsiPrintSeagateCacheLPage(device); if (gSeagateFactoryLPage) - scsiPrintSeagateFactoryLPage(fd); + scsiPrintSeagateFactoryLPage(device); } + any_output = true; } - if (con->smarterrorlog) { + if (options.smart_error_log) { if (! checkedSupportedLogPages) - scsiGetSupportedLogPages(fd); - scsiPrintErrorCounterLog(fd); - if (1 == scsiFetchControlGLTSD(fd, modese_len, 1)) + scsiGetSupportedLogPages(device); + scsiPrintErrorCounterLog(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 (con->smartselftestlog) { + if (options.smart_selftest_log) { if (! checkedSupportedLogPages) - scsiGetSupportedLogPages(fd); + scsiGetSupportedLogPages(device); res = 0; if (gSelfTestLPage) - res = scsiPrintSelfTest(fd); + 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 (con->smartbackgroundlog) { + if (options.smart_background_log) { if (! checkedSupportedLogPages) - scsiGetSupportedLogPages(fd); + scsiGetSupportedLogPages(device); res = 0; if (gBackgroundResultsLPage) - res = scsiPrintBackgroundResults(fd); + 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 (con->smartexeoffimmediate) { - if (scsiSmartDefaultSelfTest(fd)) + if (options.smart_default_selftest) { + if (scsiSmartDefaultSelfTest(device)) return returnval | FAILSMART; pout("Default Self Test Successful\n"); + any_output = true; } - if (con->smartshortcapselftest) { - if (scsiSmartShortCapSelfTest(fd)) + 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 (con->smartshortselftest ) { - if (scsiSmartShortSelfTest(fd)) + 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 (con->smartextendselftest) { - if (scsiSmartExtendSelfTest(fd)) + if (options.smart_extend_selftest) { + if (scsiSmartExtendSelfTest(device)) return returnval | FAILSMART; pout("Extended Background Self Test has begun\n"); - if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, + 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", + 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"); + pout("Use smartctl -X to abort test\n"); + any_output = true; } - if (con->smartextendcapselftest) { - if (scsiSmartExtendCapSelfTest(fd)) + if (options.smart_extend_cap_selftest) { + if (scsiSmartExtendCapSelfTest(device)) return returnval | FAILSMART; pout("Extended Foreground Self Test Successful\n"); } - if (con->smartselftestabort) { - if (scsiSmartSelfTestAbort(fd)) + 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; }