4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
9 * Additional SCSI work:
10 * Copyright (C) 2003-6 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"
42 #define GBUF_SIZE 65535
44 const char* scsiprint_c_cvsid
="$Id: scsiprint.c,v 1.107 2006/04/12 16:18:57 ballen4705 Exp $"
45 CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID
;
47 // control block which points to external global control variables
48 extern smartmonctrl
*con
;
50 // to hold onto exit code for atexit routine
51 extern int exitstatus
;
53 UINT8 gBuf
[GBUF_SIZE
];
54 #define LOG_RESP_LEN 252
55 #define LOG_RESP_LONG_LEN 8192
56 #define LOG_RESP_TAPE_ALERT_LEN 0x144
58 /* Log pages supported */
59 static int gSmartLPage
= 0; /* Informational Exceptions log page */
60 static int gTempLPage
= 0;
61 static int gSelfTestLPage
= 0;
62 static int gStartStopLPage
= 0;
63 static int gReadECounterLPage
= 0;
64 static int gWriteECounterLPage
= 0;
65 static int gVerifyECounterLPage
= 0;
66 static int gNonMediumELPage
= 0;
67 static int gLastNErrorLPage
= 0;
68 static int gTapeAlertsLPage
= 0;
69 static int gSeagateCacheLPage
= 0;
70 static int gSeagateFactoryLPage
= 0;
72 /* Mode pages supported */
73 static int gIecMPage
= 1; /* N.B. assume it until we know otherwise */
75 /* Remember last successful mode sense/select command */
76 static int modese_len
= 0;
78 // Compares failure type to policy in effect, and either exits or
79 // simply returns to the calling routine.
80 extern void failuretest(int type
, int returnvalue
);
82 static void scsiGetSupportedLogPages(int device
)
86 if ((err
= scsiLogSense(device
, SUPPORTED_LPAGES
, gBuf
,
88 if (con
->reportscsiioctl
> 0)
89 pout("Log Sense for supported pages failed [%s]\n",
94 for (i
= 4; i
< gBuf
[3] + LOGPAGEHDRSIZE
; i
++) {
97 case READ_ERROR_COUNTER_LPAGE
:
98 gReadECounterLPage
= 1;
100 case WRITE_ERROR_COUNTER_LPAGE
:
101 gWriteECounterLPage
= 1;
103 case VERIFY_ERROR_COUNTER_LPAGE
:
104 gVerifyECounterLPage
= 1;
106 case LAST_N_ERROR_LPAGE
:
107 gLastNErrorLPage
= 1;
109 case NON_MEDIUM_ERROR_LPAGE
:
110 gNonMediumELPage
= 1;
112 case TEMPERATURE_LPAGE
:
115 case STARTSTOP_CYCLE_COUNTER_LPAGE
:
118 case SELFTEST_RESULTS_LPAGE
:
124 case TAPE_ALERTS_LPAGE
:
125 gTapeAlertsLPage
= 1;
127 case SEAGATE_CACHE_LPAGE
:
128 gSeagateCacheLPage
= 1;
130 case SEAGATE_FACTORY_LPAGE
:
131 gSeagateFactoryLPage
= 1;
139 /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
140 (or at least something to report). */
141 static int scsiGetSmartData(int device
, int attribs
)
145 UINT8 currenttemp
= 0;
151 if (scsiCheckIE(device
, gSmartLPage
, gTempLPage
, &asc
, &ascq
,
152 ¤ttemp
, &triptemp
)) {
153 /* error message already announced */
158 cp
= scsiGetIEString(asc
, ascq
);
162 pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp
, asc
, ascq
);
164 } else if (gIecMPage
)
165 pout("SMART Health Status: OK\n");
167 if (attribs
&& !gTempLPage
) {
168 if (currenttemp
|| triptemp
)
171 if (255 != currenttemp
)
172 pout("Current Drive Temperature: %d C\n", currenttemp
);
174 pout("Current Drive Temperature: <not available>\n");
177 pout("Drive Trip Temperature: %d C\n", triptemp
);
183 // Returns number of logged errors or zero if none or -1 if fetching
185 static char *severities
= "CWI";
187 static int scsiGetTapeAlertsData(int device
, int peripheral_type
)
189 unsigned short pagelength
;
190 unsigned short parametercode
;
197 if ((err
= scsiLogSense(device
, TAPE_ALERTS_LPAGE
, gBuf
,
198 LOG_RESP_TAPE_ALERT_LEN
, LOG_RESP_TAPE_ALERT_LEN
))) {
199 pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err
));
203 if (gBuf
[0] != 0x2e) {
204 pout("TapeAlerts Log Sense Failed\n");
208 pagelength
= (unsigned short) gBuf
[2] << 8 | gBuf
[3];
210 for (s
=severities
; *s
; s
++) {
211 for (i
= 4; i
< pagelength
; i
+= 5) {
212 parametercode
= (unsigned short) gBuf
[i
] << 8 | gBuf
[i
+1];
215 ts
= SCSI_PT_MEDIUM_CHANGER
== peripheral_type
?
216 scsiTapeAlertsChangerDevice(parametercode
) :
217 scsiTapeAlertsTapeDevice(parametercode
);
220 pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
221 pout("[0x%02x] %s\n", parametercode
, ts
);
230 pout("TapeAlert: OK\n");
235 static void scsiGetStartStopData(int device
)
237 UINT32 currentStartStop
;
238 UINT32 recommendedStartStop
;
242 if ((err
= scsiLogSense(device
, STARTSTOP_CYCLE_COUNTER_LPAGE
, gBuf
,
245 pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err
));
249 if (gBuf
[0] != STARTSTOP_CYCLE_COUNTER_LPAGE
) {
251 pout("StartStop Log Sense Failed, page mismatch\n");
255 len
= ((gBuf
[2] << 8) | gBuf
[3]) + 4;
257 for (k
= 0; k
< 2; ++k
)
258 str
[k
] = gBuf
[12 + k
];
260 pout("Manufactured in week %s of year ", str
);
261 for (k
= 0; k
< 4; ++k
)
262 str
[k
] = gBuf
[8 + k
];
267 recommendedStartStop
= (gBuf
[28] << 24) | (gBuf
[29] << 16) |
268 (gBuf
[30] << 8) | gBuf
[31];
269 currentStartStop
= (gBuf
[36] << 24) | (gBuf
[37] << 16) |
270 (gBuf
[38] << 8) | gBuf
[39];
271 pout("Current start stop count: %u times\n", currentStartStop
);
272 if (0xffffffff != recommendedStartStop
)
273 pout("Recommended maximum start stop count: %u times\n",
274 recommendedStartStop
);
278 static void scsiPrintGrownDefectListLen(int device
)
280 int err
, dl_format
, dl_len
, div
;
283 if ((err
= scsiReadDefect10(device
, 0 /* req_plist */, 1 /* req_glist */,
284 4 /* bytes from index */, gBuf
, 4))) {
285 if (con
->reportscsiioctl
> 0) {
287 pout("Read defect list (10) Failed: %s\n", scsiErrString(err
));
292 if (0x8 != (gBuf
[1] & 0x18)) {
294 pout("Read defect list: asked for grown list but didn't get it\n");
299 dl_format
= (gBuf
[1] & 0x7);
301 case 0: /* short block */
304 case 3: /* long block */
305 case 4: /* bytes from index */
306 case 5: /* physical sector */
311 pout("defect list format %d unknown\n", dl_format
);
315 dl_len
= (gBuf
[2] << 8) + gBuf
[3];
317 pout("Elements in grown defect list: 0\n");
320 pout("Grown defect list length=%d bytes [unknown "
321 "number of elements]\n", dl_len
);
323 pout("Elements in grown defect list: %d\n", dl_len
/ div
);
327 static void scsiPrintSeagateCacheLPage(int device
)
329 int k
, j
, num
, pl
, pc
, err
, len
;
334 if ((err
= scsiLogSense(device
, SEAGATE_CACHE_LPAGE
, gBuf
,
337 pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err
));
341 if (gBuf
[0] != SEAGATE_CACHE_LPAGE
) {
343 pout("Seagate Cache Log Sense Failed, page mismatch\n");
347 len
= ((gBuf
[2] << 8) | gBuf
[3]) + 4;
351 pc
= (ucp
[0] << 8) | ucp
[1];
354 case 0: case 1: case 2: case 3: case 4:
357 if (con
->reportscsiioctl
> 0) {
359 pout("Vendor (Seagate) cache lpage has unexpected parameter"
368 pout("Vendor (Seagate) cache information\n");
372 pc
= (ucp
[0] << 8) | ucp
[1];
375 case 0: pout(" Blocks sent to initiator"); break;
376 case 1: pout(" Blocks received from initiator"); break;
377 case 2: pout(" Blocks read from cache and sent to initiator"); break;
378 case 3: pout(" Number of read and write commands whose size "
379 "<= segment size"); break;
380 case 4: pout(" Number of read and write commands whose size "
381 "> segment size"); break;
382 default: pout(" Unknown Seagate parameter code [0x%x]", pc
); break;
386 if (k
> (int)sizeof(ull
)) {
387 xp
+= (k
- (int)sizeof(ull
));
388 k
= (int)sizeof(ull
);
391 for (j
= 0; j
< k
; ++j
) {
396 pout(" = %"PRIu64
"\n", ull
);
402 static void scsiPrintSeagateFactoryLPage(int device
)
404 int k
, j
, num
, pl
, pc
, len
, err
, good
, bad
;
409 if ((err
= scsiLogSense(device
, SEAGATE_FACTORY_LPAGE
, gBuf
,
412 pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err
));
416 if (gBuf
[0] != SEAGATE_FACTORY_LPAGE
) {
418 pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
422 len
= ((gBuf
[2] << 8) | gBuf
[3]) + 4;
428 pc
= (ucp
[0] << 8) | ucp
[1];
441 if ((good
< 2) || (bad
> 4)) { /* heuristic */
442 if (con
->reportscsiioctl
> 0) {
444 pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
445 "unexpected parameters, skip\n");
450 pout("Vendor (Seagate/Hitachi) factory information\n");
454 pc
= (ucp
[0] << 8) | ucp
[1];
458 case 0: pout(" number of hours powered up");
461 case 8: pout(" number of minutes until next internal SMART test");
465 if (con
->reportscsiioctl
> 0) {
467 pout("Vendor (Seagate/Hitachi) factory lpage: "
468 "unknown parameter code [0x%x]\n", pc
);
476 if (k
> (int)sizeof(ull
)) {
477 xp
+= (k
- (int)sizeof(ull
));
478 k
= (int)sizeof(ull
);
481 for (j
= 0; j
< k
; ++j
) {
487 pout(" = %.2f\n", uint64_to_double(ull
) / 60.0 );
489 pout(" = %"PRIu64
"\n", ull
);
496 static void scsiPrintErrorCounterLog(int device
)
498 struct scsiErrorCounter errCounterArr
[3];
499 struct scsiErrorCounter
* ecp
;
500 struct scsiNonMediumError nme
;
501 int found
[3] = {0, 0, 0};
502 const char * pageNames
[3] = {"read: ", "write: ", "verify: "};
506 if (gReadECounterLPage
&& (0 == scsiLogSense(device
,
507 READ_ERROR_COUNTER_LPAGE
, gBuf
, LOG_RESP_LEN
, 0))) {
508 scsiDecodeErrCounterPage(gBuf
, &errCounterArr
[0]);
511 if (gWriteECounterLPage
&& (0 == scsiLogSense(device
,
512 WRITE_ERROR_COUNTER_LPAGE
, gBuf
, LOG_RESP_LEN
, 0))) {
513 scsiDecodeErrCounterPage(gBuf
, &errCounterArr
[1]);
516 if (gVerifyECounterLPage
&& (0 == scsiLogSense(device
,
517 VERIFY_ERROR_COUNTER_LPAGE
, gBuf
, LOG_RESP_LEN
, 0))) {
518 scsiDecodeErrCounterPage(gBuf
, &errCounterArr
[2]);
519 ecp
= &errCounterArr
[2];
520 for (k
= 0; k
< 7; ++k
) {
521 if (ecp
->gotPC
[k
] && ecp
->counter
[k
]) {
527 if (found
[0] || found
[1] || found
[2]) {
528 pout("\nError counter log:\n");
529 pout(" Errors Corrected by Total "
530 "Correction Gigabytes Total\n");
531 pout(" ECC rereads/ errors "
532 "algorithm processed uncorrected\n");
533 pout(" fast | delayed rewrites corrected "
534 "invocations [10^9 bytes] errors\n");
535 for (k
= 0; k
< 3; ++k
) {
538 ecp
= &errCounterArr
[k
];
539 pout("%s%8"PRIu64
" %8"PRIu64
" %8"PRIu64
" %8"PRIu64
" %8"PRIu64
,
540 pageNames
[k
], ecp
->counter
[0], ecp
->counter
[1],
541 ecp
->counter
[2], ecp
->counter
[3], ecp
->counter
[4]);
542 processed_gb
= uint64_to_double(ecp
->counter
[5]) / 1000000000.0;
543 pout(" %12.3f %8"PRIu64
"\n", processed_gb
, ecp
->counter
[6]);
547 pout("\nError Counter logging not supported\n");
548 if (gNonMediumELPage
&& (0 == scsiLogSense(device
,
549 NON_MEDIUM_ERROR_LPAGE
, gBuf
, LOG_RESP_LEN
, 0))) {
550 scsiDecodeNonMediumErrPage(gBuf
, &nme
);
552 pout("\nNon-medium error count: %8"PRIu64
"\n", nme
.counterPC0
);
554 pout("Track following error count [Hitachi]: %8"PRIu64
"\n",
557 pout("Positioning error count [Hitachi]: %8"PRIu64
"\n",
560 if (gLastNErrorLPage
&& (0 == scsiLogSense(device
,
561 LAST_N_ERROR_LPAGE
, gBuf
, LOG_RESP_LONG_LEN
, 0))) {
565 num
= (gBuf
[2] << 8) + gBuf
[3] + 4;
566 num
= (num
< LOG_RESP_LONG_LEN
) ? num
: LOG_RESP_LONG_LEN
;
570 pout("\nNo error events logged\n");
572 pout("\nLast n error events log page\n");
573 for (k
= num
; k
> 0; k
-= pl
, ucp
+= pl
) {
575 pout(" <<short Last n error events log page>>\n");
579 pc
= (ucp
[0] << 8) + ucp
[1];
581 if ((ucp
[2] & 0x1) && (ucp
[2] & 0x2)) {
582 pout(" Error event %d:\n", pc
);
583 pout(" [binary]:\n");
584 dStrHex((const char *)ucp
+ 4, pl
- 4, 1);
585 } else if (ucp
[2] & 0x1) {
586 pout(" Error event %d:\n", pc
);
587 pout(" %.*s\n", pl
- 4, (const char *)(ucp
+ 4));
589 if (con
->reportscsiioctl
> 0) {
590 pout(" Error event %d:\n", pc
);
591 pout(" [data counter??]:\n");
592 dStrHex((const char *)ucp
+ 4, pl
- 4, 1);
601 static const char * self_test_code
[] = {
612 static const char * self_test_result
[] = {
614 "Interrupted ('-X' switch)",
615 "Interrupted (bus reset ?)",
616 "Unknown error, incomplete",
617 "Completed, segment failed",
618 "Failed in first segment ",
619 "Failed in second segment ",
620 "Failed in segment --> ",
628 "Self test in progress ..."
631 // See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
632 // Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
633 // 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
634 // FAILSMART is returned.
635 static int scsiPrintSelfTest(int device
)
637 int num
, k
, n
, res
, err
, durationSec
;
643 if ((err
= scsiLogSense(device
, SELFTEST_RESULTS_LPAGE
, gBuf
,
644 LOG_RESP_SELF_TEST_LEN
, 0))) {
646 pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err
));
650 if (gBuf
[0] != SELFTEST_RESULTS_LPAGE
) {
652 pout("Self-test Log Sense Failed, page mismatch\n");
656 // compute page length
657 num
= (gBuf
[2] << 8) + gBuf
[3];
658 // Log sense page length 0x190 bytes
661 pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num
);
665 // loop through the twenty possible entries
666 for (k
= 0, ucp
= gBuf
+ 4; k
< 20; ++k
, ucp
+= 20 ) {
669 // timestamp in power-on hours (or zero if test in progress)
670 n
= (ucp
[6] << 8) | ucp
[7];
672 // The spec says "all 20 bytes will be zero if no test" but
673 // DG has found otherwise. So this is a heuristic.
674 if ((0 == n
) && (0 == ucp
[4]))
677 // only print header if needed
679 pout("\nSMART Self-test log\n");
680 pout("Num Test Status segment "
681 "LifeTime LBA_first_err [SK ASC ASQ]\n");
682 pout(" Description number "
687 // print parameter code (test number) & self-test code text
688 pout("#%2d %s", (ucp
[0] << 8) | ucp
[1],
689 self_test_code
[(ucp
[4] >> 5) & 0x7]);
691 // check the self-test result nibble, using the self-test results
692 // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
693 switch ((res
= ucp
[4] & 0xf)) {
695 // an unknown error occurred while the device server
696 // was processing the self-test and the device server
697 // was unable to complete the self-test
701 // the self-test completed with a failure in a test
702 // segment, and the test segment that failed is not
707 // the first segment of the self-test failed
711 // the second segment of the self-test failed
715 // another segment of the self-test failed and which
716 // test is indicated by the contents of the SELF-TEST
723 pout(" %s", self_test_result
[res
]);
725 // self-test number identifies test that failed and consists
726 // of either the number of the segment that failed during
727 // the test, or the number of the test that failed and the
728 // number of the segment in which the test was run, using a
729 // vendor-specific method of putting both numbers into a
732 pout(" %3d", (int)ucp
[5]);
736 // print time that the self-test was completed
737 if (n
==0 && res
==0xf)
738 // self-test in progress
743 // construct 8-byte integer address of first failure
744 for (i
= 0; i
< 8; i
++) {
748 // print Address of First Failure, if sensible
749 if ((~(uint64_t)0 != ull
) && (res
> 0) && (res
< 0xf)) {
752 // was hex but change to decimal to conform with ATA
753 snprintf(buff
, sizeof(buff
), "%"PRIu64
, ull
);
754 // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull);
759 // if sense key nonzero, then print it, along with
760 // additional sense code and additional sense code qualifier
762 pout(" [0x%x 0x%x 0x%x]\n", ucp
[16] & 0xf, ucp
[17], ucp
[18]);
767 // if header never printed, then there was no output
769 pout("No self-tests have been logged\n");
772 if ((0 == scsiFetchExtendedSelfTestTime(device
, &durationSec
,
773 modese_len
)) && (durationSec
> 0)) {
774 pout("Long (extended) Self Test duration: %d seconds "
775 "[%.1f minutes]\n", durationSec
, durationSec
/ 60.0);
780 static const char * peripheral_dt_arr
[] = {
796 "optical card reader"
799 static const char * transport_proto_arr
[] = {
800 "Fibre channel (FCP-2)",
801 "Parallel SCSI (SPI-4)",
818 /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
819 static int scsiGetDriveInfo(int device
, UINT8
* peripheral_type
, int all
)
821 char manufacturer
[9];
824 char timedatetz
[DATEANDEPOCHLEN
];
825 struct scsi_iec_mode_page iec
;
826 int err
, iec_err
, len
, req_len
, avail_len
, val
;
833 if ((err
= scsiStdInquiry(device
, gBuf
, req_len
))) {
835 pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err
));
836 pout("Retrying with a 64 byte Standard Inquiry\n");
838 /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
840 if ((err
= scsiStdInquiry(device
, gBuf
, req_len
))) {
842 pout("Standard Inquiry (64 bytes) failed [%s]\n",
848 avail_len
= gBuf
[4] + 5;
849 len
= (avail_len
< req_len
) ? avail_len
: req_len
;
850 peri_dt
= gBuf
[0] & 0x1f;
852 *peripheral_type
= peri_dt
;
858 pout("Short INQUIRY response, skip product id\n");
862 memset(manufacturer
, 0, sizeof(manufacturer
));
863 strncpy(manufacturer
, (char *)&gBuf
[8], 8);
865 memset(product
, 0, sizeof(product
));
866 strncpy(product
, (char *)&gBuf
[16], 16);
868 memset(revision
, 0, sizeof(revision
));
869 strncpy(revision
, (char *)&gBuf
[32], 4);
870 pout("Device: %s %s Version: %s\n", manufacturer
, product
, revision
);
872 if (0 == strncmp(manufacturer
, "3ware", 5) || 0 == strncmp(manufacturer
, "AMCC", 4) ) {
873 pout("please try adding '-d 3ware,N'\n");
874 pout("you may also need to change device to /dev/twaN or /dev/tweN\n");
876 } else if ((len
>= 42) &&
877 (0 == strncmp((const char *)(gBuf
+ 36), "MVSATA", 6))) {
878 pout("please try '-d marvell'\n");
880 } else if ((avail_len
>= 96) && (0 == strncmp(manufacturer
, "ATA", 3))) {
881 /* <<<< This is Linux specific code to detect SATA disks using a
882 SCSI-ATA command translation layer. This may be generalized
883 later when the t10.org SAT project matures. >>>> */
885 memset(gBuf
, 0, req_len
);
886 if ((err
= scsiInquiryVpd(device
, 0x83, gBuf
, req_len
))) {
888 pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n",
893 avail_len
= ((gBuf
[2] << 8) + gBuf
[3]) + 4;
894 len
= (avail_len
< req_len
) ? avail_len
: req_len
;
895 if (isLinuxLibAta(gBuf
, len
)) {
896 pout("\nIn Linux, SATA disks accessed via libata are "
897 "only supported by smartmontools\n"
898 "for kernel versions 2.6.15 and above. Try "
899 "an additional '-d ata' argument.\n");
904 /* Do this here to try and detect badly conforming devices (some USB
905 keys) that will lock up on a InquiryVpd or log sense or ... */
906 if ((iec_err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
907 if (SIMPLE_ERR_BAD_RESP
== iec_err
) {
908 pout(">> Terminate command early due to bad response to IEC "
915 modese_len
= iec
.modese_len
;
917 if (0 == (err
= scsiInquiryVpd(device
, 0x80, gBuf
, 64))) {
918 /* should use VPD page 0x83 and fall back to this page (0x80)
919 * if 0x83 not supported. NAA requires a lot of decoding code */
921 gBuf
[4 + len
] = '\0';
922 pout("Serial number: %s\n", &gBuf
[4]);
924 else if (con
->reportscsiioctl
> 0) {
926 if (SIMPLE_ERR_BAD_RESP
== err
)
927 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
929 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err
);
933 // print SCSI peripheral device type
934 if (peri_dt
< (int)(sizeof(peripheral_dt_arr
) /
935 sizeof(peripheral_dt_arr
[0])))
936 pout("Device type: %s\n", peripheral_dt_arr
[peri_dt
]);
938 pout("Device type: <%d>\n", peri_dt
);
940 // See if transport protocol is known
941 val
= scsiFetchTransportProtocol(device
, modese_len
);
942 if ((val
>= 0) && (val
<= 0xf))
943 pout("Transport protocol: %s\n", transport_proto_arr
[val
]);
945 // print current time and date and timezone
946 dateandtimezone(timedatetz
);
947 pout("Local Time is: %s\n", timedatetz
);
949 if ((SCSI_PT_SEQUENTIAL_ACCESS
== *peripheral_type
) ||
950 (SCSI_PT_MEDIUM_CHANGER
== *peripheral_type
))
952 // See if unit accepts SCSI commmands from us
953 if ((err
= scsiTestUnitReady(device
))) {
954 if (SIMPLE_ERR_NOT_READY
== err
) {
957 pout("device is NOT READY (e.g. spun down, busy)\n");
959 pout("device is NOT READY (e.g. no tape)\n");
961 } else if (SIMPLE_ERR_NO_MEDIUM
== err
) {
963 pout("NO MEDIUM present on device\n");
965 } else if (SIMPLE_ERR_BECOMING_READY
== err
) {
967 pout("device becoming ready (wait)\n");
971 pout("device Test Unit Ready [%s]\n", scsiErrString(err
));
974 failuretest(MANDATORY_CMD
, returnval
|=FAILID
);
980 pout("Device does not support SMART");
981 if (con
->reportscsiioctl
> 0)
982 pout(" [%s]\n", scsiErrString(iec_err
));
992 pout("Device supports SMART and is %s\n",
993 (scsi_IsExceptionControlEnabled(&iec
)) ? "Enabled" : "Disabled");
994 pout("%s\n", (scsi_IsWarningEnabled(&iec
)) ?
995 "Temperature Warning Enabled" :
996 "Temperature Warning Disabled or Not Supported");
1000 static int scsiSmartEnable(int device
)
1002 struct scsi_iec_mode_page iec
;
1005 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1007 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1008 scsiErrString(err
));
1012 modese_len
= iec
.modese_len
;
1014 if ((err
= scsiSetExceptionControlAndWarning(device
, 1, &iec
))) {
1016 pout("unable to enable Exception control and warning [%s]\n",
1017 scsiErrString(err
));
1021 /* Need to refetch 'iec' since could be modified by previous call */
1022 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1023 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1024 scsiErrString(err
));
1027 modese_len
= iec
.modese_len
;
1029 pout("Informational Exceptions (SMART) %s\n",
1030 scsi_IsExceptionControlEnabled(&iec
) ? "enabled" : "disabled");
1031 pout("Temperature warning %s\n",
1032 scsi_IsWarningEnabled(&iec
) ? "enabled" : "disabled");
1036 static int scsiSmartDisable(int device
)
1038 struct scsi_iec_mode_page iec
;
1041 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1043 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1044 scsiErrString(err
));
1048 modese_len
= iec
.modese_len
;
1050 if ((err
= scsiSetExceptionControlAndWarning(device
, 0, &iec
))) {
1052 pout("unable to disable Exception control and warning [%s]\n",
1053 scsiErrString(err
));
1057 /* Need to refetch 'iec' since could be modified by previous call */
1058 if ((err
= scsiFetchIECmpage(device
, &iec
, modese_len
))) {
1059 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1060 scsiErrString(err
));
1063 modese_len
= iec
.modese_len
;
1065 pout("Informational Exceptions (SMART) %s\n",
1066 scsi_IsExceptionControlEnabled(&iec
) ? "enabled" : "disabled");
1067 pout("Temperature warning %s\n",
1068 scsi_IsWarningEnabled(&iec
) ? "enabled" : "disabled");
1072 static void scsiPrintTemp(int device
)
1077 if (scsiGetTemp(device
, &temp
, &trip
))
1082 pout("Current Drive Temperature: %d C\n", temp
);
1084 pout("Current Drive Temperature: <not available>\n");
1087 pout("Drive Trip Temperature: %d C\n", trip
);
1090 /* Main entry point used by smartctl command. Return 0 for success */
1091 int scsiPrintMain(int fd
)
1093 int checkedSupportedLogPages
= 0;
1094 UINT8 peripheral_type
= 0;
1096 int res
, durationSec
;
1098 res
= scsiGetDriveInfo(fd
, &peripheral_type
, con
->driveinfo
);
1103 failuretest(MANDATORY_CMD
, returnval
|= FAILID
);
1106 if (con
->smartenable
) {
1107 if (scsiSmartEnable(fd
))
1108 failuretest(MANDATORY_CMD
, returnval
|= FAILSMART
);
1111 if (con
->smartdisable
) {
1112 if (scsiSmartDisable(fd
))
1113 failuretest(MANDATORY_CMD
,returnval
|= FAILSMART
);
1116 if (con
->smartautosaveenable
) {
1117 if (scsiSetControlGLTSD(fd
, 0, modese_len
)) {
1118 pout("Enable autosave (clear GLTSD bit) failed\n");
1119 failuretest(OPTIONAL_CMD
,returnval
|= FAILSMART
);
1123 if (con
->smartautosavedisable
) {
1124 if (scsiSetControlGLTSD(fd
, 1, modese_len
)) {
1125 pout("Disable autosave (set GLTSD bit) failed\n");
1126 failuretest(OPTIONAL_CMD
,returnval
|= FAILSMART
);
1130 if (con
->checksmart
) {
1131 scsiGetSupportedLogPages(fd
);
1132 checkedSupportedLogPages
= 1;
1133 if ((SCSI_PT_SEQUENTIAL_ACCESS
== peripheral_type
) ||
1134 (SCSI_PT_MEDIUM_CHANGER
== peripheral_type
)) { /* tape device */
1135 if (gTapeAlertsLPage
) {
1137 pout("TapeAlert Supported\n");
1138 if (-1 == scsiGetTapeAlertsData(fd
, peripheral_type
))
1139 failuretest(OPTIONAL_CMD
, returnval
|= FAILSMART
);
1142 pout("TapeAlert Not Supported\n");
1143 } else { /* disk, cd/dvd, enclosure, etc */
1144 if ((res
= scsiGetSmartData(fd
, con
->smartvendorattrib
))) {
1146 returnval
|= FAILSTATUS
;
1148 returnval
|= FAILSMART
;
1152 if (con
->smartvendorattrib
) {
1153 if (! checkedSupportedLogPages
)
1154 scsiGetSupportedLogPages(fd
);
1156 if (con
->checksmart
)
1160 if (gStartStopLPage
)
1161 scsiGetStartStopData(fd
);
1162 if (SCSI_PT_DIRECT_ACCESS
== peripheral_type
) {
1163 scsiPrintGrownDefectListLen(fd
);
1164 if (gSeagateCacheLPage
)
1165 scsiPrintSeagateCacheLPage(fd
);
1166 if (gSeagateFactoryLPage
)
1167 scsiPrintSeagateFactoryLPage(fd
);
1170 if (con
->smarterrorlog
) {
1171 if (! checkedSupportedLogPages
)
1172 scsiGetSupportedLogPages(fd
);
1173 scsiPrintErrorCounterLog(fd
);
1174 if (1 == scsiFetchControlGLTSD(fd
, modese_len
, 1))
1175 pout("\n[GLTSD (Global Logging Target Save Disable) set. "
1176 "Enable Save with '-S on']\n");
1178 if (con
->smartselftestlog
) {
1179 if (! checkedSupportedLogPages
)
1180 scsiGetSupportedLogPages(fd
);
1183 res
= scsiPrintSelfTest(fd
);
1185 pout("Device does not support Self Test logging\n");
1186 failuretest(OPTIONAL_CMD
, returnval
|=FAILSMART
);
1189 failuretest(OPTIONAL_CMD
, returnval
|=res
);
1191 if (con
->smartexeoffimmediate
) {
1192 if (scsiSmartDefaultSelfTest(fd
))
1193 return returnval
| FAILSMART
;
1194 pout("Default Self Test Successful\n");
1196 if (con
->smartshortcapselftest
) {
1197 if (scsiSmartShortCapSelfTest(fd
))
1198 return returnval
| FAILSMART
;
1199 pout("Short Foreground Self Test Successful\n");
1201 if (con
->smartshortselftest
) {
1202 if (scsiSmartShortSelfTest(fd
))
1203 return returnval
| FAILSMART
;
1204 pout("Short Background Self Test has begun\n");
1205 pout("Use smartctl -X to abort test\n");
1207 if (con
->smartextendselftest
) {
1208 if (scsiSmartExtendSelfTest(fd
))
1209 return returnval
| FAILSMART
;
1210 pout("Extended Background Self Test has begun\n");
1211 if ((0 == scsiFetchExtendedSelfTestTime(fd
, &durationSec
,
1212 modese_len
)) && (durationSec
> 0)) {
1213 time_t t
= time(NULL
);
1216 pout("Please wait %d minutes for test to complete.\n",
1218 pout("Estimated completion time: %s\n", ctime(&t
));
1220 pout("Use smartctl -X to abort test\n");
1222 if (con
->smartextendcapselftest
) {
1223 if (scsiSmartExtendCapSelfTest(fd
))
1224 return returnval
| FAILSMART
;
1225 pout("Extended Foreground Self Test Successful\n");
1227 if (con
->smartselftestabort
) {
1228 if (scsiSmartSelfTestAbort(fd
))
1229 return returnval
| FAILSMART
;
1230 pout("Self Test returned without error\n");