4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2002-7 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
9 * Additional SCSI work:
10 * Copyright (C) 2003-7 Douglas Gilbert <dougg@torque.net>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2, or (at your option)
17 * You should have received a copy of the GNU General Public License
18 * (for example COPYING); if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * This code was originally developed as a Senior Thesis by Michael Cornwell
22 * at the Concurrent Systems Laboratory (now part of the Storage Systems
23 * Research Center), Jack Baskin School of Engineering, University of
24 * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
38 #include "scsiprint.h"
43 #define GBUF_SIZE 65535
45 const char* scsiprint_c_cvsid
="$Id: scsiprint.cpp,v 1.120 2007/07/21 20:59:41 chrfranke Exp $"
46 CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID
;
48 // control block which points to external global control variables
49 extern smartmonctrl
*con
;
51 // to hold onto exit code for atexit routine
52 extern int exitstatus
;
54 UINT8 gBuf
[GBUF_SIZE
];
55 #define LOG_RESP_LEN 252
56 #define LOG_RESP_LONG_LEN 16384
57 #define LOG_RESP_TAPE_ALERT_LEN 0x144
59 /* Log pages supported */
60 static int gSmartLPage
= 0; /* Informational Exceptions log page */
61 static int gTempLPage
= 0;
62 static int gSelfTestLPage
= 0;
63 static int gStartStopLPage
= 0;
64 static int gReadECounterLPage
= 0;
65 static int gWriteECounterLPage
= 0;
66 static int gVerifyECounterLPage
= 0;
67 static int gNonMediumELPage
= 0;
68 static int gLastNErrorLPage
= 0;
69 static int gBackgroundResultsLPage
= 0;
70 static int gTapeAlertsLPage
= 0;
71 static int gSeagateCacheLPage
= 0;
72 static int gSeagateFactoryLPage
= 0;
74 /* Mode pages supported */
75 static int gIecMPage
= 1; /* N.B. assume it until we know otherwise */
77 /* Remember last successful mode sense/select command */
78 static int modese_len
= 0;
80 // Compares failure type to policy in effect, and either exits or
81 // simply returns to the calling routine.
82 extern void failuretest(int type
, int returnvalue
);
84 static void scsiGetSupportedLogPages(int device
)
88 if ((err
= scsiLogSense(device
, SUPPORTED_LPAGES
, 0, gBuf
,
90 if (con
->reportscsiioctl
> 0)
91 pout("Log Sense for supported pages failed [%s]\n",
96 for (i
= 4; i
< gBuf
[3] + LOGPAGEHDRSIZE
; i
++) {
99 case READ_ERROR_COUNTER_LPAGE
:
100 gReadECounterLPage
= 1;
102 case WRITE_ERROR_COUNTER_LPAGE
:
103 gWriteECounterLPage
= 1;
105 case VERIFY_ERROR_COUNTER_LPAGE
:
106 gVerifyECounterLPage
= 1;
108 case LAST_N_ERROR_LPAGE
:
109 gLastNErrorLPage
= 1;
111 case NON_MEDIUM_ERROR_LPAGE
:
112 gNonMediumELPage
= 1;
114 case TEMPERATURE_LPAGE
:
117 case STARTSTOP_CYCLE_COUNTER_LPAGE
:
120 case SELFTEST_RESULTS_LPAGE
:
126 case BACKGROUND_RESULTS_LPAGE
:
127 gBackgroundResultsLPage
= 1;
129 case TAPE_ALERTS_LPAGE
:
130 gTapeAlertsLPage
= 1;
132 case SEAGATE_CACHE_LPAGE
:
133 gSeagateCacheLPage
= 1;
135 case SEAGATE_FACTORY_LPAGE
:
136 gSeagateFactoryLPage
= 1;
144 /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
145 (or at least something to report). */
146 static int scsiGetSmartData(int device
, int attribs
)
150 UINT8 currenttemp
= 0;
156 if (scsiCheckIE(device
, gSmartLPage
, gTempLPage
, &asc
, &ascq
,
157 ¤ttemp
, &triptemp
)) {
158 /* error message already announced */
163 cp
= scsiGetIEString(asc
, ascq
);
167 pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp
, asc
, ascq
);
169 } else if (gIecMPage
)
170 pout("SMART Health Status: OK\n");
172 if (attribs
&& !gTempLPage
) {
173 if (currenttemp
|| triptemp
)
176 if (255 != currenttemp
)
177 pout("Current Drive Temperature: %d C\n", currenttemp
);
179 pout("Current Drive Temperature: <not available>\n");
182 pout("Drive Trip Temperature: %d C\n", triptemp
);
188 // Returns number of logged errors or zero if none or -1 if fetching
190 static char *severities
= "CWI";
192 static int scsiGetTapeAlertsData(int device
, int peripheral_type
)
194 unsigned short pagelength
;
195 unsigned short parametercode
;
202 if ((err
= scsiLogSense(device
, TAPE_ALERTS_LPAGE
, 0, gBuf
,
203 LOG_RESP_TAPE_ALERT_LEN
, LOG_RESP_TAPE_ALERT_LEN
))) {
204 pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err
));
208 if (gBuf
[0] != 0x2e) {
209 pout("TapeAlerts Log Sense Failed\n");
213 pagelength
= (unsigned short) gBuf
[2] << 8 | gBuf
[3];
215 for (s
=severities
; *s
; s
++) {
216 for (i
= 4; i
< pagelength
; i
+= 5) {
217 parametercode
= (unsigned short) gBuf
[i
] << 8 | gBuf
[i
+1];
220 ts
= SCSI_PT_MEDIUM_CHANGER
== peripheral_type
?
221 scsiTapeAlertsChangerDevice(parametercode
) :
222 scsiTapeAlertsTapeDevice(parametercode
);
225 pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
226 pout("[0x%02x] %s\n", parametercode
, ts
);
235 pout("TapeAlert: OK\n");
240 static void scsiGetStartStopData(int device
)
243 int err
, len
, k
, extra
, pc
;
246 if ((err
= scsiLogSense(device
, STARTSTOP_CYCLE_COUNTER_LPAGE
, 0, gBuf
,
249 pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err
));
253 if ((gBuf
[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE
) {
255 pout("StartStop Log Sense Failed, page mismatch\n");
259 len
= ((gBuf
[2] << 8) | gBuf
[3]);
261 for (k
= len
; k
> 0; k
-= extra
, ucp
+= extra
) {
264 pout("StartStop Log Sense Failed: short\n");
269 pc
= (ucp
[0] << 8) + ucp
[1];
273 pout("Manufactured in week %.2s of year %.4s\n", ucp
+ 8,
277 /* ignore Accounting date */
281 u
= (ucp
[4] << 24) | (ucp
[5] << 16) | (ucp
[6] << 8) | ucp
[7];
283 pout("Recommended maximum start stop count: %u times\n",
289 u
= (ucp
[4] << 24) | (ucp
[5] << 16) | (ucp
[6] << 8) | ucp
[7];
291 pout("Current start stop count: %u times\n", u
);
301 static void scsiPrintGrownDefectListLen(int device
)
303 int err
, dl_format
, dl_len
, div
;
306 if ((err
= scsiReadDefect10(device
, 0 /* req_plist */, 1 /* req_glist */,
307 4 /* bytes from index */, gBuf
, 4))) {
308 if (con
->reportscsiioctl
> 0) {
310 pout("Read defect list (10) Failed: %s\n", scsiErrString(err
));
315 if (0x8 != (gBuf
[1] & 0x18)) {
317 pout("Read defect list: asked for grown list but didn't get it\n");
322 dl_format
= (gBuf
[1] & 0x7);
324 case 0: /* short block */
327 case 3: /* long block */
328 case 4: /* bytes from index */
329 case 5: /* physical sector */
334 pout("defect list format %d unknown\n", dl_format
);
338 dl_len
= (gBuf
[2] << 8) + gBuf
[3];
340 pout("Elements in grown defect list: 0\n");
343 pout("Grown defect list length=%d bytes [unknown "
344 "number of elements]\n", dl_len
);
346 pout("Elements in grown defect list: %d\n", dl_len
/ div
);
350 static void scsiPrintSeagateCacheLPage(int device
)
352 int k
, j
, num
, pl
, pc
, err
, len
;
357 if ((err
= scsiLogSense(device
, SEAGATE_CACHE_LPAGE
, 0, gBuf
,
360 pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err
));
364 if ((gBuf
[0] & 0x3f) != SEAGATE_CACHE_LPAGE
) {
366 pout("Seagate Cache Log Sense Failed, page mismatch\n");
370 len
= ((gBuf
[2] << 8) | gBuf
[3]) + 4;
374 pc
= (ucp
[0] << 8) | ucp
[1];
377 case 0: case 1: case 2: case 3: case 4:
380 if (con
->reportscsiioctl
> 0) {
382 pout("Vendor (Seagate) cache lpage has unexpected parameter"
391 pout("Vendor (Seagate) cache information\n");
395 pc
= (ucp
[0] << 8) | ucp
[1];
398 case 0: pout(" Blocks sent to initiator"); break;
399 case 1: pout(" Blocks received from initiator"); break;
400 case 2: pout(" Blocks read from cache and sent to initiator"); break;
401 case 3: pout(" Number of read and write commands whose size "
402 "<= segment size"); break;
403 case 4: pout(" Number of read and write commands whose size "
404 "> segment size"); break;
405 default: pout(" Unknown Seagate parameter code [0x%x]", pc
); break;
409 if (k
> (int)sizeof(ull
)) {
410 xp
+= (k
- (int)sizeof(ull
));
411 k
= (int)sizeof(ull
);
414 for (j
= 0; j
< k
; ++j
) {
419 pout(" = %"PRIu64
"\n", ull
);
425 static void scsiPrintSeagateFactoryLPage(int device
)
427 int k
, j
, num
, pl
, pc
, len
, err
, good
, bad
;
432 if ((err
= scsiLogSense(device
, SEAGATE_FACTORY_LPAGE
, 0, gBuf
,
435 pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err
));
439 if ((gBuf
[0] & 0x3f) != SEAGATE_FACTORY_LPAGE
) {
441 pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
445 len
= ((gBuf
[2] << 8) | gBuf
[3]) + 4;
451 pc
= (ucp
[0] << 8) | ucp
[1];
464 if ((good
< 2) || (bad
> 4)) { /* heuristic */
465 if (con
->reportscsiioctl
> 0) {
467 pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
468 "unexpected parameters, skip\n");
473 pout("Vendor (Seagate/Hitachi) factory information\n");
477 pc
= (ucp
[0] << 8) | ucp
[1];
481 case 0: pout(" number of hours powered up");
484 case 8: pout(" number of minutes until next internal SMART test");
488 if (con
->reportscsiioctl
> 0) {
490 pout("Vendor (Seagate/Hitachi) factory lpage: "
491 "unknown parameter code [0x%x]\n", pc
);
499 if (k
> (int)sizeof(ull
)) {
500 xp
+= (k
- (int)sizeof(ull
));
501 k
= (int)sizeof(ull
);
504 for (j
= 0; j
< k
; ++j
) {
510 pout(" = %.2f\n", uint64_to_double(ull
) / 60.0 );
512 pout(" = %"PRIu64
"\n", ull
);
519 static void scsiPrintErrorCounterLog(int device
)
521 struct scsiErrorCounter errCounterArr
[3];
522 struct scsiErrorCounter
* ecp
;
523 struct scsiNonMediumError nme
;
524 int found
[3] = {0, 0, 0};
525 const char * pageNames
[3] = {"read: ", "write: ", "verify: "};
529 if (gReadECounterLPage
&& (0 == scsiLogSense(device
,
530 READ_ERROR_COUNTER_LPAGE
, 0, gBuf
, LOG_RESP_LEN
, 0))) {
531 scsiDecodeErrCounterPage(gBuf
, &errCounterArr
[0]);
534 if (gWriteECounterLPage
&& (0 == scsiLogSense(device
,
535 WRITE_ERROR_COUNTER_LPAGE
, 0, gBuf
, LOG_RESP_LEN
, 0))) {
536 scsiDecodeErrCounterPage(gBuf
, &errCounterArr
[1]);
539 if (gVerifyECounterLPage
&& (0 == scsiLogSense(device
,
540 VERIFY_ERROR_COUNTER_LPAGE
, 0, gBuf
, LOG_RESP_LEN
, 0))) {
541 scsiDecodeErrCounterPage(gBuf
, &errCounterArr
[2]);
542 ecp
= &errCounterArr
[2];
543 for (k
= 0; k
< 7; ++k
) {
544 if (ecp
->gotPC
[k
] && ecp
->counter
[k
]) {
550 if (found
[0] || found
[1] || found
[2]) {
551 pout("\nError counter log:\n");
552 pout(" Errors Corrected by Total "
553 "Correction Gigabytes Total\n");
554 pout(" ECC rereads/ errors "
555 "algorithm processed uncorrected\n");
556 pout(" fast | delayed rewrites corrected "
557 "invocations [10^9 bytes] errors\n");
558 for (k
= 0; k
< 3; ++k
) {
561 ecp
= &errCounterArr
[k
];
562 pout("%s%8"PRIu64
" %8"PRIu64
" %8"PRIu64
" %8"PRIu64
" %8"PRIu64
,
563 pageNames
[k
], ecp
->counter
[0], ecp
->counter
[1],
564 ecp
->counter
[2], ecp
->counter
[3], ecp
->counter
[4]);
565 processed_gb
= uint64_to_double(ecp
->counter
[5]) / 1000000000.0;
566 pout(" %12.3f %8"PRIu64
"\n", processed_gb
, ecp
->counter
[6]);
570 pout("\nError Counter logging not supported\n");
571 if (gNonMediumELPage
&& (0 == scsiLogSense(device
,
572 NON_MEDIUM_ERROR_LPAGE
, 0, gBuf
, LOG_RESP_LEN
, 0))) {
573 scsiDecodeNonMediumErrPage(gBuf
, &nme
);
575 pout("\nNon-medium error count: %8"PRIu64
"\n", nme
.counterPC0
);
577 pout("Track following error count [Hitachi]: %8"PRIu64
"\n",
580 pout("Positioning error count [Hitachi]: %8"PRIu64
"\n",
583 if (gLastNErrorLPage
&& (0 == scsiLogSense(device
,
584 LAST_N_ERROR_LPAGE
, 0, gBuf
, LOG_RESP_LONG_LEN
, 0))) {
586 int num
, k
, pc
, pl
, truncated
;
588 num
= (gBuf
[2] << 8) + gBuf
[3] + 4;
589 truncated
= (num
> LOG_RESP_LONG_LEN
) ? num
: 0;
591 num
= LOG_RESP_LONG_LEN
;
595 pout("\nNo error events logged\n");
597 pout("\nLast n error events log page\n");
598 for (k
= num
; k
> 0; k
-= pl
, ucp
+= pl
) {
600 pout(" <<short Last n error events log page>>\n");
604 pc
= (ucp
[0] << 8) + ucp
[1];
606 if ((ucp
[2] & 0x1) && (ucp
[2] & 0x2)) {
607 pout(" Error event %d:\n", pc
);
608 pout(" [binary]:\n");
609 dStrHex((const char *)ucp
+ 4, pl
- 4, 1);
610 } else if (ucp
[2] & 0x1) {
611 pout(" Error event %d:\n", pc
);
612 pout(" %.*s\n", pl
- 4, (const char *)(ucp
+ 4));
614 if (con
->reportscsiioctl
> 0) {
615 pout(" Error event %d:\n", pc
);
616 pout(" [data counter??]:\n");
617 dStrHex((const char *)ucp
+ 4, pl
- 4, 1);
623 pout(" >>>> log truncated, fetched %d of %d available "
624 "bytes\n", LOG_RESP_LONG_LEN
, truncated
);
629 static const char * self_test_code
[] = {
640 static const char * self_test_result
[] = {
642 "Interrupted ('-X' switch)",
643 "Interrupted (bus reset ?)",
644 "Unknown error, incomplete",
645 "Completed, segment failed",
646 "Failed in first segment ",
647 "Failed in second segment ",
648 "Failed in segment --> ",
656 "Self test in progress ..."
659 // See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
660 // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
661 // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
662 // FAILSMART is returned.
663 static int scsiPrintSelfTest(int device
)
665 int num
, k
, n
, res
, err
, durationSec
;
671 if ((err
= scsiLogSense(device
, SELFTEST_RESULTS_LPAGE
, 0, gBuf
,
672 LOG_RESP_SELF_TEST_LEN
, 0))) {
674 pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err
));
678 if ((gBuf
[0] & 0x3f) != SELFTEST_RESULTS_LPAGE
) {
680 pout("Self-test Log Sense Failed, page mismatch\n");
684 // compute page length
685 num
= (gBuf
[2] << 8) + gBuf
[3];
686 // Log sense page length 0x190 bytes
689 pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num
);
693 // loop through the twenty possible entries
694 for (k
= 0, ucp
= gBuf
+ 4; k
< 20; ++k
, ucp
+= 20 ) {
697 // timestamp in power-on hours (or zero if test in progress)
698 n
= (ucp
[6] << 8) | ucp
[7];
700 // The spec says "all 20 bytes will be zero if no test" but
701 // DG has found otherwise. So this is a heuristic.
702 if ((0 == n
) && (0 == ucp
[4]))
705 // only print header if needed
707 pout("\nSMART Self-test log\n");
708 pout("Num Test Status segment "
709 "LifeTime LBA_first_err [SK ASC ASQ]\n");
710 pout(" Description number "
715 // print parameter code (test number) & self-test code text
716 pout("#%2d %s", (ucp
[0] << 8) | ucp
[1],
717 self_test_code
[(ucp
[4] >> 5) & 0x7]);
719 // check the self-test result nibble, using the self-test results
720 // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
721 switch ((res
= ucp
[4] & 0xf)) {
723 // an unknown error occurred while the device server
724 // was processing the self-test and the device server
725 // was unable to complete the self-test
729 // the self-test completed with a failure in a test
730 // segment, and the test segment that failed is not
735 // the first segment of the self-test failed
739 // the second segment of the self-test failed
743 // another segment of the self-test failed and which
744 // test is indicated by the contents of the SELF-TEST
751 pout(" %s", self_test_result
[res
]);
753 // self-test number identifies test that failed and consists
754 // of either the number of the segment that failed during
755 // the test, or the number of the test that failed and the
756 // number of the segment in which the test was run, using a
757 // vendor-specific method of putting both numbers into a
760 pout(" %3d", (int)ucp
[5]);
764 // print time that the self-test was completed
765 if (n
==0 && res
==0xf)
766 // self-test in progress
771 // construct 8-byte integer address of first failure
772 for (i
= 0; i
< 8; i
++) {
776 // print Address of First Failure, if sensible
777 if ((~(uint64_t)0 != ull
) && (res
> 0) && (res
< 0xf)) {
780 // was hex but change to decimal to conform with ATA
781 snprintf(buff
, sizeof(buff
), "%"PRIu64
, ull
);
782 // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull);
787 // if sense key nonzero, then print it, along with
788 // additional sense code and additional sense code qualifier
790 pout(" [0x%x 0x%x 0x%x]\n", ucp
[16] & 0xf, ucp
[17], ucp
[18]);
795 // if header never printed, then there was no output
797 pout("No self-tests have been logged\n");
800 if ((0 == scsiFetchExtendedSelfTestTime(device
, &durationSec
,
801 modese_len
)) && (durationSec
> 0)) {
802 pout("Long (extended) Self Test duration: %d seconds "
803 "[%.1f minutes]\n", durationSec
, durationSec
/ 60.0);
808 static const char * bms_status
[] = {
811 "pre-scan is active",
812 "halted due to fatal error",
813 "halted due to a vendor specific pattern of error",
814 "halted due to medium formatted without P-List",
815 "halted - vendor specific cause",
816 "halted due to temperature out of range",
817 "halted until BM interval timer expires", /* 8 */
820 static const char * reassign_status
[] = {
821 "No reassignment needed",
822 "Require Reassign or Write command",
823 "Successfully reassigned",
826 "Recovered via rewrite in-place",
827 "Reassigned by app, has valid data",
828 "Reassigned by app, has no valid data",
829 "Unsuccessfully reassigned by app", /* 8 */
832 // See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 .
833 // Returns 0 if ok else FAIL* bitmask. Note can have a status entry
834 // and up to 2048 events (although would hope to have less). May set
835 // FAILLOG if serious errors detected (in the future).
836 static int scsiPrintBackgroundResults(int device
)
838 int num
, j
, m
, err
, pc
, pl
, truncated
;
844 if ((err
= scsiLogSense(device
, BACKGROUND_RESULTS_LPAGE
, 0, gBuf
,
845 LOG_RESP_LONG_LEN
, 0))) {
847 pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err
));
851 if ((gBuf
[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE
) {
853 pout("Background scan results Log Sense Failed, page mismatch\n");
857 // compute page length
858 num
= (gBuf
[2] << 8) + gBuf
[3] + 4;
861 pout("Background scan results Log Sense length is %d, no scan "
866 truncated
= (num
> LOG_RESP_LONG_LEN
) ? num
: 0;
868 num
= LOG_RESP_LONG_LEN
;
872 pc
= (ucp
[0] << 8) | ucp
[1];
879 pout("\nBackground scan results log\n");
882 if ((pl
< 16) || (num
< 16)) {
887 if (j
< (int)(sizeof(bms_status
) / sizeof(bms_status
[0])))
888 pout("%s\n", bms_status
[j
]);
890 pout("unknown [0x%x] background scan status value\n", j
);
891 j
= (ucp
[4] << 24) + (ucp
[5] << 16) + (ucp
[6] << 8) + ucp
[7];
892 pout(" Accumulated power on time, hours:minutes %d:%02d "
893 "[%d minutes]\n", (j
/ 60), (j
% 60), j
);
894 pout(" Number of background scans performed: %d, ",
895 (ucp
[10] << 8) + ucp
[11]);
896 pout("scan progress: %.2f%%\n",
897 (double)((ucp
[12] << 8) + ucp
[13]) * 100.0 / 65536.0);
902 pout("\nBackground scan results log\n");
906 pout("\n # when lba(hex) [sk,asc,ascq] "
907 "reassign_status\n");
910 if ((pl
< 24) || (num
< 24)) {
912 pout("parameter length >= 24 expected, got %d\n", pl
);
915 j
= (ucp
[4] << 24) + (ucp
[5] << 16) + (ucp
[6] << 8) + ucp
[7];
916 pout("%4d:%02d ", (j
/ 60), (j
% 60));
917 for (m
= 0; m
< 8; ++m
)
918 pout("%02x", ucp
[16 + m
]);
919 pout(" [%x,%x,%x] ", ucp
[8] & 0xf, ucp
[9], ucp
[10]);
920 j
= (ucp
[8] >> 4) & 0xf;
922 (int)(sizeof(reassign_status
) / sizeof(reassign_status
[0])))
923 pout("%s\n", reassign_status
[j
]);
925 pout("Reassign status: reserved [0x%x]\n", j
);
932 pout(" >>>> log truncated, fetched %d of %d available "
933 "bytes\n", LOG_RESP_LONG_LEN
, truncated
);
937 static const char * peripheral_dt_arr
[] = {
953 "optical card reader"
956 static const char * transport_proto_arr
[] = {
957 "Fibre channel (FCP-2)",
958 "Parallel SCSI (SPI-4)",
975 /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
976 static int scsiGetDriveInfo(int device
, UINT8
* peripheral_type
, int all
)
978 char manufacturer
[9];
981 char timedatetz
[DATEANDEPOCHLEN
];
982 struct scsi_iec_mode_page iec
;
983 int err
, iec_err
, len
, req_len
, avail_len
, val
;
990 if ((err
= scsiStdInquiry(device
, gBuf
, req_len
))) {
992 pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err
));
993 pout("Retrying with a 64 byte Standard Inquiry\n");
995 /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
997 if ((err
= scsiStdInquiry(device
, gBuf
, req_len
))) {
999 pout("Standard Inquiry (64 bytes) failed [%s]\n",
1000 scsiErrString(err
));
1005 avail_len
= gBuf
[4] + 5;
1006 len
= (avail_len
< req_len
) ? avail_len
: req_len
;
1007 peri_dt
= gBuf
[0] & 0x1f;
1008 if (peripheral_type
)
1009 *peripheral_type
= peri_dt
;
1013 pout("Short INQUIRY response, skip product id\n");
1017 memset(manufacturer
, 0, sizeof(manufacturer
));
1018 strncpy(manufacturer
, (char *)&gBuf
[8], 8);
1020 memset(product
, 0, sizeof(product
));
1021 strncpy(product
, (char *)&gBuf
[16], 16);
1023 memset(revision
, 0, sizeof(revision
));
1024 strncpy(revision
, (char *)&gBuf
[32], 4);
1025 if (all
&& (0 != strncmp(manufacturer
, "ATA", 3)))
1026 pout("Device: %s %s Version: %s\n", manufacturer
, product
, revision
);
1028 if (0 == strncmp(manufacturer
, "3ware", 5) || 0 == strncmp(manufacturer
, "AMCC", 4) ) {
1029 #if defined(_WIN32) || defined(__CYGWIN__)
1030 pout("please try changing device to /dev/hdX,N\n");
1032 pout("please try adding '-d 3ware,N'\n");
1033 pout("you may also need to change device to /dev/twaN or /dev/tweN\n");
1036 } else if ((len
>= 42) &&
1037 (0 == strncmp((const char *)(gBuf
+ 36), "MVSATA", 6))) {
1038 pout("please try '-d marvell'\n");
1040 } else if ((0 == con
->controller_explicit
) &&
1041 (0 == strncmp(manufacturer
, "ATA ", 8)) &&
1042 has_sat_pass_through(device
, 0)) {
1043 con
->controller_type
= CONTROLLER_SAT
;
1044 if (con
->reportscsiioctl
> 0) {
1046 pout("Detected SAT interface, switch to device type 'sat'\n");
1050 } else if ((0 == con
->controller_explicit
) &&
1051 (0 == strncmp(manufacturer
, "ATA", 3))) {
1052 pout("\nProbable ATA device behind a SAT layer\n"
1053 "Try an additional '-d ata' or '-d sat' argument.\n");
1059 /* Do this here to try and detect badly conforming devices (some USB
1060 keys) that will lock up on a InquiryVpd or log sense or ... */
1061 if ((iec_err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1062 if (SIMPLE_ERR_BAD_RESP
== iec_err
) {
1063 pout(">> Terminate command early due to bad response to IEC "
1070 modese_len
= iec
.modese_len
;
1072 if (!con
->dont_print_serial
) {
1073 if (0 == (err
= scsiInquiryVpd(device
, 0x80, gBuf
, 64))) {
1074 /* should use VPD page 0x83 and fall back to this page (0x80)
1075 * if 0x83 not supported. NAA requires a lot of decoding code */
1077 gBuf
[4 + len
] = '\0';
1078 pout("Serial number: %s\n", &gBuf
[4]);
1080 else if (con
->reportscsiioctl
> 0) {
1082 if (SIMPLE_ERR_BAD_RESP
== err
)
1083 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
1085 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err
);
1090 // print SCSI peripheral device type
1091 if (peri_dt
< (int)(sizeof(peripheral_dt_arr
) /
1092 sizeof(peripheral_dt_arr
[0])))
1093 pout("Device type: %s\n", peripheral_dt_arr
[peri_dt
]);
1095 pout("Device type: <%d>\n", peri_dt
);
1097 // See if transport protocol is known
1098 val
= scsiFetchTransportProtocol(device
, modese_len
);
1099 if ((val
>= 0) && (val
<= 0xf))
1100 pout("Transport protocol: %s\n", transport_proto_arr
[val
]);
1102 // print current time and date and timezone
1103 dateandtimezone(timedatetz
);
1104 pout("Local Time is: %s\n", timedatetz
);
1106 if ((SCSI_PT_SEQUENTIAL_ACCESS
== *peripheral_type
) ||
1107 (SCSI_PT_MEDIUM_CHANGER
== *peripheral_type
))
1109 // See if unit accepts SCSI commmands from us
1110 if ((err
= scsiTestUnitReady(device
))) {
1111 if (SIMPLE_ERR_NOT_READY
== err
) {
1114 pout("device is NOT READY (e.g. spun down, busy)\n");
1116 pout("device is NOT READY (e.g. no tape)\n");
1118 } else if (SIMPLE_ERR_NO_MEDIUM
== err
) {
1120 pout("NO MEDIUM present on device\n");
1122 } else if (SIMPLE_ERR_BECOMING_READY
== err
) {
1124 pout("device becoming ready (wait)\n");
1128 pout("device Test Unit Ready [%s]\n", scsiErrString(err
));
1131 failuretest(MANDATORY_CMD
, returnval
|=FAILID
);
1137 pout("Device does not support SMART");
1138 if (con
->reportscsiioctl
> 0)
1139 pout(" [%s]\n", scsiErrString(iec_err
));
1149 pout("Device supports SMART and is %s\n",
1150 (scsi_IsExceptionControlEnabled(&iec
)) ? "Enabled" : "Disabled");
1151 pout("%s\n", (scsi_IsWarningEnabled(&iec
)) ?
1152 "Temperature Warning Enabled" :
1153 "Temperature Warning Disabled or Not Supported");
1157 static int scsiSmartEnable(int device
)
1159 struct scsi_iec_mode_page iec
;
1162 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1164 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1165 scsiErrString(err
));
1169 modese_len
= iec
.modese_len
;
1171 if ((err
= scsiSetExceptionControlAndWarning(device
, 1, &iec
))) {
1173 pout("unable to enable Exception control and warning [%s]\n",
1174 scsiErrString(err
));
1178 /* Need to refetch 'iec' since could be modified by previous call */
1179 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1180 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1181 scsiErrString(err
));
1184 modese_len
= iec
.modese_len
;
1186 pout("Informational Exceptions (SMART) %s\n",
1187 scsi_IsExceptionControlEnabled(&iec
) ? "enabled" : "disabled");
1188 pout("Temperature warning %s\n",
1189 scsi_IsWarningEnabled(&iec
) ? "enabled" : "disabled");
1193 static int scsiSmartDisable(int device
)
1195 struct scsi_iec_mode_page iec
;
1198 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1200 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1201 scsiErrString(err
));
1205 modese_len
= iec
.modese_len
;
1207 if ((err
= scsiSetExceptionControlAndWarning(device
, 0, &iec
))) {
1209 pout("unable to disable Exception control and warning [%s]\n",
1210 scsiErrString(err
));
1214 /* Need to refetch 'iec' since could be modified by previous call */
1215 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1216 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1217 scsiErrString(err
));
1220 modese_len
= iec
.modese_len
;
1222 pout("Informational Exceptions (SMART) %s\n",
1223 scsi_IsExceptionControlEnabled(&iec
) ? "enabled" : "disabled");
1224 pout("Temperature warning %s\n",
1225 scsi_IsWarningEnabled(&iec
) ? "enabled" : "disabled");
1229 static void scsiPrintTemp(int device
)
1234 if (scsiGetTemp(device
, &temp
, &trip
))
1239 pout("Current Drive Temperature: %d C\n", temp
);
1241 pout("Current Drive Temperature: <not available>\n");
1244 pout("Drive Trip Temperature: %d C\n", trip
);
1247 /* Main entry point used by smartctl command. Return 0 for success */
1248 int scsiPrintMain(int fd
)
1250 int checkedSupportedLogPages
= 0;
1251 UINT8 peripheral_type
= 0;
1253 int res
, durationSec
;
1255 res
= scsiGetDriveInfo(fd
, &peripheral_type
, con
->driveinfo
);
1260 failuretest(MANDATORY_CMD
, returnval
|= FAILID
);
1263 if (con
->smartenable
) {
1264 if (scsiSmartEnable(fd
))
1265 failuretest(MANDATORY_CMD
, returnval
|= FAILSMART
);
1268 if (con
->smartdisable
) {
1269 if (scsiSmartDisable(fd
))
1270 failuretest(MANDATORY_CMD
,returnval
|= FAILSMART
);
1273 if (con
->smartautosaveenable
) {
1274 if (scsiSetControlGLTSD(fd
, 0, modese_len
)) {
1275 pout("Enable autosave (clear GLTSD bit) failed\n");
1276 failuretest(OPTIONAL_CMD
,returnval
|= FAILSMART
);
1280 if (con
->smartautosavedisable
) {
1281 if (scsiSetControlGLTSD(fd
, 1, modese_len
)) {
1282 pout("Disable autosave (set GLTSD bit) failed\n");
1283 failuretest(OPTIONAL_CMD
,returnval
|= FAILSMART
);
1287 if (con
->checksmart
) {
1288 scsiGetSupportedLogPages(fd
);
1289 checkedSupportedLogPages
= 1;
1290 if ((SCSI_PT_SEQUENTIAL_ACCESS
== peripheral_type
) ||
1291 (SCSI_PT_MEDIUM_CHANGER
== peripheral_type
)) { /* tape device */
1292 if (gTapeAlertsLPage
) {
1294 pout("TapeAlert Supported\n");
1295 if (-1 == scsiGetTapeAlertsData(fd
, peripheral_type
))
1296 failuretest(OPTIONAL_CMD
, returnval
|= FAILSMART
);
1299 pout("TapeAlert Not Supported\n");
1300 } else { /* disk, cd/dvd, enclosure, etc */
1301 if ((res
= scsiGetSmartData(fd
, con
->smartvendorattrib
))) {
1303 returnval
|= FAILSTATUS
;
1305 returnval
|= FAILSMART
;
1309 if (con
->smartvendorattrib
) {
1310 if (! checkedSupportedLogPages
)
1311 scsiGetSupportedLogPages(fd
);
1313 if (con
->checksmart
)
1317 if (gStartStopLPage
)
1318 scsiGetStartStopData(fd
);
1319 if (SCSI_PT_DIRECT_ACCESS
== peripheral_type
) {
1320 scsiPrintGrownDefectListLen(fd
);
1321 if (gSeagateCacheLPage
)
1322 scsiPrintSeagateCacheLPage(fd
);
1323 if (gSeagateFactoryLPage
)
1324 scsiPrintSeagateFactoryLPage(fd
);
1327 if (con
->smarterrorlog
) {
1328 if (! checkedSupportedLogPages
)
1329 scsiGetSupportedLogPages(fd
);
1330 scsiPrintErrorCounterLog(fd
);
1331 if (1 == scsiFetchControlGLTSD(fd
, modese_len
, 1))
1332 pout("\n[GLTSD (Global Logging Target Save Disable) set. "
1333 "Enable Save with '-S on']\n");
1335 if (con
->smartselftestlog
) {
1336 if (! checkedSupportedLogPages
)
1337 scsiGetSupportedLogPages(fd
);
1340 res
= scsiPrintSelfTest(fd
);
1342 pout("Device does not support Self Test logging\n");
1343 failuretest(OPTIONAL_CMD
, returnval
|=FAILSMART
);
1346 failuretest(OPTIONAL_CMD
, returnval
|=res
);
1348 if (con
->smartbackgroundlog
) {
1349 if (! checkedSupportedLogPages
)
1350 scsiGetSupportedLogPages(fd
);
1352 if (gBackgroundResultsLPage
)
1353 res
= scsiPrintBackgroundResults(fd
);
1355 pout("Device does not support Background scan results logging\n");
1356 failuretest(OPTIONAL_CMD
, returnval
|=FAILSMART
);
1359 failuretest(OPTIONAL_CMD
, returnval
|=res
);
1361 if (con
->smartexeoffimmediate
) {
1362 if (scsiSmartDefaultSelfTest(fd
))
1363 return returnval
| FAILSMART
;
1364 pout("Default Self Test Successful\n");
1366 if (con
->smartshortcapselftest
) {
1367 if (scsiSmartShortCapSelfTest(fd
))
1368 return returnval
| FAILSMART
;
1369 pout("Short Foreground Self Test Successful\n");
1371 if (con
->smartshortselftest
) {
1372 if (scsiSmartShortSelfTest(fd
))
1373 return returnval
| FAILSMART
;
1374 pout("Short Background Self Test has begun\n");
1375 pout("Use smartctl -X to abort test\n");
1377 if (con
->smartextendselftest
) {
1378 if (scsiSmartExtendSelfTest(fd
))
1379 return returnval
| FAILSMART
;
1380 pout("Extended Background Self Test has begun\n");
1381 if ((0 == scsiFetchExtendedSelfTestTime(fd
, &durationSec
,
1382 modese_len
)) && (durationSec
> 0)) {
1383 time_t t
= time(NULL
);
1386 pout("Please wait %d minutes for test to complete.\n",
1388 pout("Estimated completion time: %s\n", ctime(&t
));
1390 pout("Use smartctl -X to abort test\n");
1392 if (con
->smartextendcapselftest
) {
1393 if (scsiSmartExtendCapSelfTest(fd
))
1394 return returnval
| FAILSMART
;
1395 pout("Extended Foreground Self Test Successful\n");
1397 if (con
->smartselftestabort
) {
1398 if (scsiSmartSelfTestAbort(fd
))
1399 return returnval
| FAILSMART
;
1400 pout("Self Test returned without error\n");