]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - scsiprint.c
add debian specific patches
[mirror_smartmontools-debian.git] / scsiprint.c
CommitLineData
832b75ed
GG
1/*
2 * scsiprint.c
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
6 * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
7 * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
8 *
9 * Additional SCSI work:
10 * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
11 *
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)
15 * any later version.
16 *
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.
20 *
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/
25 *
26 */
27
28
29#include <stdio.h>
30#include <string.h>
31#include <fcntl.h>
32#include <errno.h>
33
34#include "config.h"
35#include "int64.h"
36#include "extern.h"
37#include "scsicmds.h"
38#include "scsiprint.h"
39#include "smartctl.h"
40#include "utility.h"
41
42#define GBUF_SIZE 65535
43
44const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.107 2006/04/12 16:18:57 ballen4705 Exp $"
45CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
46
47// control block which points to external global control variables
48extern smartmonctrl *con;
49
50// to hold onto exit code for atexit routine
51extern int exitstatus;
52
53UINT8 gBuf[GBUF_SIZE];
54#define LOG_RESP_LEN 252
55#define LOG_RESP_LONG_LEN 8192
56#define LOG_RESP_TAPE_ALERT_LEN 0x144
57
58/* Log pages supported */
59static int gSmartLPage = 0; /* Informational Exceptions log page */
60static int gTempLPage = 0;
61static int gSelfTestLPage = 0;
62static int gStartStopLPage = 0;
63static int gReadECounterLPage = 0;
64static int gWriteECounterLPage = 0;
65static int gVerifyECounterLPage = 0;
66static int gNonMediumELPage = 0;
67static int gLastNErrorLPage = 0;
68static int gTapeAlertsLPage = 0;
69static int gSeagateCacheLPage = 0;
70static int gSeagateFactoryLPage = 0;
71
72/* Mode pages supported */
73static int gIecMPage = 1; /* N.B. assume it until we know otherwise */
74
75/* Remember last successful mode sense/select command */
76static int modese_len = 0;
77
78// Compares failure type to policy in effect, and either exits or
79// simply returns to the calling routine.
80extern void failuretest(int type, int returnvalue);
81
82static void scsiGetSupportedLogPages(int device)
83{
84 int i, err;
85
86 if ((err = scsiLogSense(device, SUPPORTED_LPAGES, gBuf,
87 LOG_RESP_LEN, 0))) {
88 if (con->reportscsiioctl > 0)
89 pout("Log Sense for supported pages failed [%s]\n",
90 scsiErrString(err));
91 return;
92 }
93
94 for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
95 switch (gBuf[i])
96 {
97 case READ_ERROR_COUNTER_LPAGE:
98 gReadECounterLPage = 1;
99 break;
100 case WRITE_ERROR_COUNTER_LPAGE:
101 gWriteECounterLPage = 1;
102 break;
103 case VERIFY_ERROR_COUNTER_LPAGE:
104 gVerifyECounterLPage = 1;
105 break;
106 case LAST_N_ERROR_LPAGE:
107 gLastNErrorLPage = 1;
108 break;
109 case NON_MEDIUM_ERROR_LPAGE:
110 gNonMediumELPage = 1;
111 break;
112 case TEMPERATURE_LPAGE:
113 gTempLPage = 1;
114 break;
115 case STARTSTOP_CYCLE_COUNTER_LPAGE:
116 gStartStopLPage = 1;
117 break;
118 case SELFTEST_RESULTS_LPAGE:
119 gSelfTestLPage = 1;
120 break;
121 case IE_LPAGE:
122 gSmartLPage = 1;
123 break;
124 case TAPE_ALERTS_LPAGE:
125 gTapeAlertsLPage = 1;
126 break;
127 case SEAGATE_CACHE_LPAGE:
128 gSeagateCacheLPage = 1;
129 break;
130 case SEAGATE_FACTORY_LPAGE:
131 gSeagateFactoryLPage = 1;
132 break;
133 default:
134 break;
135 }
136 }
137}
138
139/* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
140 (or at least something to report). */
141static int scsiGetSmartData(int device, int attribs)
142{
143 UINT8 asc;
144 UINT8 ascq;
145 UINT8 currenttemp = 0;
146 UINT8 triptemp = 0;
147 const char * cp;
148 int err = 0;
149
150 PRINT_ON(con);
151 if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
152 &currenttemp, &triptemp)) {
153 /* error message already announced */
154 PRINT_OFF(con);
155 return -1;
156 }
157 PRINT_OFF(con);
158 cp = scsiGetIEString(asc, ascq);
159 if (cp) {
160 err = -2;
161 PRINT_ON(con);
162 pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
163 PRINT_OFF(con);
164 } else if (gIecMPage)
165 pout("SMART Health Status: OK\n");
166
167 if (attribs && !gTempLPage) {
168 if (currenttemp || triptemp)
169 pout("\n");
170 if (currenttemp) {
171 if (255 != currenttemp)
172 pout("Current Drive Temperature: %d C\n", currenttemp);
173 else
174 pout("Current Drive Temperature: <not available>\n");
175 }
176 if (triptemp)
177 pout("Drive Trip Temperature: %d C\n", triptemp);
178 }
179 return err;
180}
181
182
183// Returns number of logged errors or zero if none or -1 if fetching
184// TapeAlerts fails
185static char *severities = "CWI";
186
187static int scsiGetTapeAlertsData(int device, int peripheral_type)
188{
189 unsigned short pagelength;
190 unsigned short parametercode;
191 int i, err;
192 char *s;
193 const char *ts;
194 int failures = 0;
195
196 PRINT_ON(con);
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));
200 PRINT_OFF(con);
201 return -1;
202 }
203 if (gBuf[0] != 0x2e) {
204 pout("TapeAlerts Log Sense Failed\n");
205 PRINT_OFF(con);
206 return -1;
207 }
208 pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
209
210 for (s=severities; *s; s++) {
211 for (i = 4; i < pagelength; i += 5) {
212 parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
213
214 if (gBuf[i + 4]) {
215 ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
216 scsiTapeAlertsChangerDevice(parametercode) :
217 scsiTapeAlertsTapeDevice(parametercode);
218 if (*ts == *s) {
219 if (!failures)
220 pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
221 pout("[0x%02x] %s\n", parametercode, ts);
222 failures += 1;
223 }
224 }
225 }
226 }
227 PRINT_OFF(con);
228
229 if (! failures)
230 pout("TapeAlert: OK\n");
231
232 return failures;
233}
234
235static void scsiGetStartStopData(int device)
236{
237 UINT32 currentStartStop;
238 UINT32 recommendedStartStop;
239 int err, len, k;
240 char str[6];
241
242 if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, gBuf,
243 LOG_RESP_LEN, 0))) {
244 PRINT_ON(con);
245 pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
246 PRINT_OFF(con);
247 return;
248 }
249 if (gBuf[0] != STARTSTOP_CYCLE_COUNTER_LPAGE) {
250 PRINT_ON(con);
251 pout("StartStop Log Sense Failed, page mismatch\n");
252 PRINT_OFF(con);
253 return;
254 }
255 len = ((gBuf[2] << 8) | gBuf[3]) + 4;
256 if (len > 13) {
257 for (k = 0; k < 2; ++k)
258 str[k] = gBuf[12 + k];
259 str[k] = '\0';
260 pout("Manufactured in week %s of year ", str);
261 for (k = 0; k < 4; ++k)
262 str[k] = gBuf[8 + k];
263 str[k] = '\0';
264 pout("%s\n", str);
265 }
266 if (len > 39) {
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);
275 }
276}
277
278static void scsiPrintGrownDefectListLen(int device)
279{
280 int err, dl_format, dl_len, div;
281
282 memset(gBuf, 0, 4);
283 if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
284 4 /* bytes from index */, gBuf, 4))) {
285 if (con->reportscsiioctl > 0) {
286 PRINT_ON(con);
287 pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
288 PRINT_OFF(con);
289 }
290 return;
291 }
292 if (0x8 != (gBuf[1] & 0x18)) {
293 PRINT_ON(con);
294 pout("Read defect list: asked for grown list but didn't get it\n");
295 PRINT_OFF(con);
296 return;
297 }
298 div = 0;
299 dl_format = (gBuf[1] & 0x7);
300 switch (dl_format) {
301 case 0: /* short block */
302 div = 4;
303 break;
304 case 3: /* long block */
305 case 4: /* bytes from index */
306 case 5: /* physical sector */
307 div = 8;
308 break;
309 default:
310 PRINT_ON(con);
311 pout("defect list format %d unknown\n", dl_format);
312 PRINT_OFF(con);
313 break;
314 }
315 dl_len = (gBuf[2] << 8) + gBuf[3];
316 if (0 == dl_len)
317 pout("Elements in grown defect list: 0\n");
318 else {
319 if (0 == div)
320 pout("Grown defect list length=%d bytes [unknown "
321 "number of elements]\n", dl_len);
322 else
323 pout("Elements in grown defect list: %d\n", dl_len / div);
324 }
325}
326
327static void scsiPrintSeagateCacheLPage(int device)
328{
329 int k, j, num, pl, pc, err, len;
330 unsigned char * ucp;
331 unsigned char * xp;
332 uint64_t ull;
333
334 if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, gBuf,
335 LOG_RESP_LEN, 0))) {
336 PRINT_ON(con);
337 pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
338 PRINT_OFF(con);
339 return;
340 }
341 if (gBuf[0] != SEAGATE_CACHE_LPAGE) {
342 PRINT_ON(con);
343 pout("Seagate Cache Log Sense Failed, page mismatch\n");
344 PRINT_OFF(con);
345 return;
346 }
347 len = ((gBuf[2] << 8) | gBuf[3]) + 4;
348 num = len - 4;
349 ucp = &gBuf[0] + 4;
350 while (num > 3) {
351 pc = (ucp[0] << 8) | ucp[1];
352 pl = ucp[3] + 4;
353 switch (pc) {
354 case 0: case 1: case 2: case 3: case 4:
355 break;
356 default:
357 if (con->reportscsiioctl > 0) {
358 PRINT_ON(con);
359 pout("Vendor (Seagate) cache lpage has unexpected parameter"
360 ", skip\n");
361 PRINT_OFF(con);
362 }
363 return;
364 }
365 num -= pl;
366 ucp += pl;
367 }
368 pout("Vendor (Seagate) cache information\n");
369 num = len - 4;
370 ucp = &gBuf[0] + 4;
371 while (num > 3) {
372 pc = (ucp[0] << 8) | ucp[1];
373 pl = ucp[3] + 4;
374 switch (pc) {
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;
383 }
384 k = pl - 4;
385 xp = ucp + 4;
386 if (k > (int)sizeof(ull)) {
387 xp += (k - (int)sizeof(ull));
388 k = (int)sizeof(ull);
389 }
390 ull = 0;
391 for (j = 0; j < k; ++j) {
392 if (j > 0)
393 ull <<= 8;
394 ull |= xp[j];
395 }
396 pout(" = %"PRIu64"\n", ull);
397 num -= pl;
398 ucp += pl;
399 }
400}
401
402static void scsiPrintSeagateFactoryLPage(int device)
403{
404 int k, j, num, pl, pc, len, err, good, bad;
405 unsigned char * ucp;
406 unsigned char * xp;
407 uint64_t ull;
408
409 if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, gBuf,
410 LOG_RESP_LEN, 0))) {
411 PRINT_ON(con);
412 pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
413 PRINT_OFF(con);
414 return;
415 }
416 if (gBuf[0] != SEAGATE_FACTORY_LPAGE) {
417 PRINT_ON(con);
418 pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
419 PRINT_OFF(con);
420 return;
421 }
422 len = ((gBuf[2] << 8) | gBuf[3]) + 4;
423 num = len - 4;
424 ucp = &gBuf[0] + 4;
425 good = 0;
426 bad = 0;
427 while (num > 3) {
428 pc = (ucp[0] << 8) | ucp[1];
429 pl = ucp[3] + 4;
430 switch (pc) {
431 case 0: case 8:
432 ++good;
433 break;
434 default:
435 ++bad;
436 break;
437 }
438 num -= pl;
439 ucp += pl;
440 }
441 if ((good < 2) || (bad > 4)) { /* heuristic */
442 if (con->reportscsiioctl > 0) {
443 PRINT_ON(con);
444 pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
445 "unexpected parameters, skip\n");
446 PRINT_OFF(con);
447 }
448 return;
449 }
450 pout("Vendor (Seagate/Hitachi) factory information\n");
451 num = len - 4;
452 ucp = &gBuf[0] + 4;
453 while (num > 3) {
454 pc = (ucp[0] << 8) | ucp[1];
455 pl = ucp[3] + 4;
456 good = 0;
457 switch (pc) {
458 case 0: pout(" number of hours powered up");
459 good = 1;
460 break;
461 case 8: pout(" number of minutes until next internal SMART test");
462 good = 1;
463 break;
464 default:
465 if (con->reportscsiioctl > 0) {
466 PRINT_ON(con);
467 pout("Vendor (Seagate/Hitachi) factory lpage: "
468 "unknown parameter code [0x%x]\n", pc);
469 PRINT_OFF(con);
470 }
471 break;
472 }
473 if (good) {
474 k = pl - 4;
475 xp = ucp + 4;
476 if (k > (int)sizeof(ull)) {
477 xp += (k - (int)sizeof(ull));
478 k = (int)sizeof(ull);
479 }
480 ull = 0;
481 for (j = 0; j < k; ++j) {
482 if (j > 0)
483 ull <<= 8;
484 ull |= xp[j];
485 }
486 if (0 == pc)
487 pout(" = %.2f\n", uint64_to_double(ull) / 60.0 );
488 else
489 pout(" = %"PRIu64"\n", ull);
490 }
491 num -= pl;
492 ucp += pl;
493 }
494}
495
496static void scsiPrintErrorCounterLog(int device)
497{
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: "};
503 int k;
504 double processed_gb;
505
506 if (gReadECounterLPage && (0 == scsiLogSense(device,
507 READ_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
508 scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
509 found[0] = 1;
510 }
511 if (gWriteECounterLPage && (0 == scsiLogSense(device,
512 WRITE_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
513 scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
514 found[1] = 1;
515 }
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]) {
522 found[2] = 1;
523 break;
524 }
525 }
526 }
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) {
536 if (! found[k])
537 continue;
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]);
544 }
545 }
546 else
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);
551 if (nme.gotPC0)
552 pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0);
553 if (nme.gotTFE_H)
554 pout("Track following error count [Hitachi]: %8"PRIu64"\n",
555 nme.counterTFE_H);
556 if (nme.gotPE_H)
557 pout("Positioning error count [Hitachi]: %8"PRIu64"\n",
558 nme.counterPE_H);
559 }
560 if (gLastNErrorLPage && (0 == scsiLogSense(device,
561 LAST_N_ERROR_LPAGE, gBuf, LOG_RESP_LONG_LEN, 0))) {
562 unsigned char * ucp;
563 int num, k, pc, pl;
564
565 num = (gBuf[2] << 8) + gBuf[3] + 4;
566 num = (num < LOG_RESP_LONG_LEN) ? num : LOG_RESP_LONG_LEN;
567 ucp = gBuf + 4;
568 num -= 4;
569 if (num < 4)
570 pout("\nNo error events logged\n");
571 else {
572 pout("\nLast n error events log page\n");
573 for (k = num; k > 0; k -= pl, ucp += pl) {
574 if (k < 3) {
575 pout(" <<short Last n error events log page>>\n");
576 break;
577 }
578 pl = ucp[3] + 4;
579 pc = (ucp[0] << 8) + ucp[1];
580 if (pl > 4) {
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));
588 } else {
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);
593 }
594 }
595 }
596 }
597 }
598 }
599}
600
601static const char * self_test_code[] = {
602 "Default ",
603 "Background short",
604 "Background long ",
605 "Reserved(3) ",
606 "Abort background",
607 "Foreground short",
608 "Foreground long ",
609 "Reserved(7) "
610};
611
612static const char * self_test_result[] = {
613 "Completed ",
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 --> ",
621 "Reserved(8) ",
622 "Reserved(9) ",
623 "Reserved(10) ",
624 "Reserved(11) ",
625 "Reserved(12) ",
626 "Reserved(13) ",
627 "Reserved(14) ",
628 "Self test in progress ..."
629};
630
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.
635static int scsiPrintSelfTest(int device)
636{
637 int num, k, n, res, err, durationSec;
638 int noheader = 1;
639 int retval = 0;
640 UINT8 * ucp;
641 uint64_t ull=0;
642
643 if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, gBuf,
644 LOG_RESP_SELF_TEST_LEN, 0))) {
645 PRINT_ON(con);
646 pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
647 PRINT_OFF(con);
648 return FAILSMART;
649 }
650 if (gBuf[0] != SELFTEST_RESULTS_LPAGE) {
651 PRINT_ON(con);
652 pout("Self-test Log Sense Failed, page mismatch\n");
653 PRINT_OFF(con);
654 return FAILSMART;
655 }
656 // compute page length
657 num = (gBuf[2] << 8) + gBuf[3];
658 // Log sense page length 0x190 bytes
659 if (num != 0x190) {
660 PRINT_ON(con);
661 pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
662 PRINT_OFF(con);
663 return FAILSMART;
664 }
665 // loop through the twenty possible entries
666 for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
667 int i;
668
669 // timestamp in power-on hours (or zero if test in progress)
670 n = (ucp[6] << 8) | ucp[7];
671
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]))
675 break;
676
677 // only print header if needed
678 if (noheader) {
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 "
683 "(hours)\n");
684 noheader=0;
685 }
686
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]);
690
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)) {
694 case 0x3:
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
698 retval|=FAILSMART;
699 break;
700 case 0x4:
701 // the self-test completed with a failure in a test
702 // segment, and the test segment that failed is not
703 // known
704 retval|=FAILLOG;
705 break;
706 case 0x5:
707 // the first segment of the self-test failed
708 retval|=FAILLOG;
709 break;
710 case 0x6:
711 // the second segment of the self-test failed
712 retval|=FAILLOG;
713 break;
714 case 0x7:
715 // another segment of the self-test failed and which
716 // test is indicated by the contents of the SELF-TEST
717 // NUMBER field
718 retval|=FAILLOG;
719 break;
720 default:
721 break;
722 }
723 pout(" %s", self_test_result[res]);
724
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
730 // single byte.
731 if (ucp[5])
732 pout(" %3d", (int)ucp[5]);
733 else
734 pout(" -");
735
736 // print time that the self-test was completed
737 if (n==0 && res==0xf)
738 // self-test in progress
739 pout(" NOW");
740 else
741 pout(" %5d", n);
742
743 // construct 8-byte integer address of first failure
744 for (i = 0; i < 8; i++) {
745 ull <<= 8;
746 ull |= ucp[i+8];
747 }
748 // print Address of First Failure, if sensible
749 if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
750 char buff[32];
751
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);
755 pout("%18s", buff);
756 } else
757 pout(" -");
758
759 // if sense key nonzero, then print it, along with
760 // additional sense code and additional sense code qualifier
761 if (ucp[16] & 0xf)
762 pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
763 else
764 pout(" [- - -]\n");
765 }
766
767 // if header never printed, then there was no output
768 if (noheader)
769 pout("No self-tests have been logged\n");
770 else
771 pout("\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);
776 }
777 return retval;
778}
779
780static const char * peripheral_dt_arr[] = {
781 "disk",
782 "tape",
783 "printer",
784 "processor",
785 "optical disk(4)",
786 "CD/DVD",
787 "scanner",
788 "optical disk(7)",
789 "medium changer",
790 "communications",
791 "graphics(10)",
792 "graphics(11)",
793 "storage array",
794 "enclosure",
795 "simplified disk",
796 "optical card reader"
797};
798
799static const char * transport_proto_arr[] = {
800 "Fibre channel (FCP-2)",
801 "Parallel SCSI (SPI-4)",
802 "SSA",
803 "IEEE 1394 (SBP-2)",
804 "RDMA (SRP)",
805 "iSCSI",
806 "SAS",
807 "ADT",
808 "0x8",
809 "0x9",
810 "0xa",
811 "0xb",
812 "0xc",
813 "0xd",
814 "0xe",
815 "0xf"
816};
817
818/* Returns 0 on success, 1 on general error and 2 for early, clean exit */
819static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
820{
821 char manufacturer[9];
822 char product[17];
823 char revision[5];
824 char timedatetz[DATEANDEPOCHLEN];
825 struct scsi_iec_mode_page iec;
826 int err, iec_err, len, req_len, avail_len, val;
827 int is_tape = 0;
828 int peri_dt = 0;
829 int returnval=0;
830
831 memset(gBuf, 0, 96);
832 req_len = 36;
833 if ((err = scsiStdInquiry(device, gBuf, req_len))) {
834 PRINT_ON(con);
835 pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
836 pout("Retrying with a 64 byte Standard Inquiry\n");
837 PRINT_OFF(con);
838 /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
839 req_len = 64;
840 if ((err = scsiStdInquiry(device, gBuf, req_len))) {
841 PRINT_ON(con);
842 pout("Standard Inquiry (64 bytes) failed [%s]\n",
843 scsiErrString(err));
844 PRINT_OFF(con);
845 return 1;
846 }
847 }
848 avail_len = gBuf[4] + 5;
849 len = (avail_len < req_len) ? avail_len : req_len;
850 peri_dt = gBuf[0] & 0x1f;
851 if (peripheral_type)
852 *peripheral_type = peri_dt;
853 if (! all)
854 return 0;
855
856 if (len < 36) {
857 PRINT_ON(con);
858 pout("Short INQUIRY response, skip product id\n");
859 PRINT_OFF(con);
860 return 1;
861 }
862 memset(manufacturer, 0, sizeof(manufacturer));
863 strncpy(manufacturer, (char *)&gBuf[8], 8);
864
865 memset(product, 0, sizeof(product));
866 strncpy(product, (char *)&gBuf[16], 16);
867
868 memset(revision, 0, sizeof(revision));
869 strncpy(revision, (char *)&gBuf[32], 4);
870 pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
871
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");
875 return 2;
876 } else if ((len >= 42) &&
877 (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) {
878 pout("please try '-d marvell'\n");
879 return 2;
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. >>>> */
884 req_len = 96;
885 memset(gBuf, 0, req_len);
886 if ((err = scsiInquiryVpd(device, 0x83, gBuf, req_len))) {
887 PRINT_ON(con);
888 pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n",
889 scsiErrString(err));
890 PRINT_OFF(con);
891 return 1;
892 }
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");
900 return 2;
901 }
902 }
903
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 "
909 "mode page\n");
910 PRINT_OFF(con);
911 gIecMPage = 0;
912 return 1;
913 }
914 } else
915 modese_len = iec.modese_len;
916
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 */
920 len = gBuf[3];
921 gBuf[4 + len] = '\0';
922 pout("Serial number: %s\n", &gBuf[4]);
923 }
924 else if (con->reportscsiioctl > 0) {
925 PRINT_ON(con);
926 if (SIMPLE_ERR_BAD_RESP == err)
927 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
928 else
929 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
930 PRINT_OFF(con);
931 }
932
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]);
937 else
938 pout("Device type: <%d>\n", peri_dt);
939
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]);
944
945 // print current time and date and timezone
946 dateandtimezone(timedatetz);
947 pout("Local Time is: %s\n", timedatetz);
948
949 if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
950 (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
951 is_tape = 1;
952 // See if unit accepts SCSI commmands from us
953 if ((err = scsiTestUnitReady(device))) {
954 if (SIMPLE_ERR_NOT_READY == err) {
955 PRINT_ON(con);
956 if (!is_tape)
957 pout("device is NOT READY (e.g. spun down, busy)\n");
958 else
959 pout("device is NOT READY (e.g. no tape)\n");
960 PRINT_OFF(con);
961 } else if (SIMPLE_ERR_NO_MEDIUM == err) {
962 PRINT_ON(con);
963 pout("NO MEDIUM present on device\n");
964 PRINT_OFF(con);
965 } else if (SIMPLE_ERR_BECOMING_READY == err) {
966 PRINT_ON(con);
967 pout("device becoming ready (wait)\n");
968 PRINT_OFF(con);
969 } else {
970 PRINT_ON(con);
971 pout("device Test Unit Ready [%s]\n", scsiErrString(err));
972 PRINT_OFF(con);
973 }
974 failuretest(MANDATORY_CMD, returnval|=FAILID);
975 }
976
977 if (iec_err) {
978 if (!is_tape) {
979 PRINT_ON(con);
980 pout("Device does not support SMART");
981 if (con->reportscsiioctl > 0)
982 pout(" [%s]\n", scsiErrString(iec_err));
983 else
984 pout("\n");
985 PRINT_OFF(con);
986 }
987 gIecMPage = 0;
988 return 0;
989 }
990
991 if (!is_tape)
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");
997 return 0;
998}
999
1000static int scsiSmartEnable(int device)
1001{
1002 struct scsi_iec_mode_page iec;
1003 int err;
1004
1005 if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
1006 PRINT_ON(con);
1007 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1008 scsiErrString(err));
1009 PRINT_OFF(con);
1010 return 1;
1011 } else
1012 modese_len = iec.modese_len;
1013
1014 if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
1015 PRINT_ON(con);
1016 pout("unable to enable Exception control and warning [%s]\n",
1017 scsiErrString(err));
1018 PRINT_OFF(con);
1019 return 1;
1020 }
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));
1025 return 1;
1026 } else
1027 modese_len = iec.modese_len;
1028
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");
1033 return 0;
1034}
1035
1036static int scsiSmartDisable(int device)
1037{
1038 struct scsi_iec_mode_page iec;
1039 int err;
1040
1041 if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
1042 PRINT_ON(con);
1043 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1044 scsiErrString(err));
1045 PRINT_OFF(con);
1046 return 1;
1047 } else
1048 modese_len = iec.modese_len;
1049
1050 if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
1051 PRINT_ON(con);
1052 pout("unable to disable Exception control and warning [%s]\n",
1053 scsiErrString(err));
1054 PRINT_OFF(con);
1055 return 1;
1056 }
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));
1061 return 1;
1062 } else
1063 modese_len = iec.modese_len;
1064
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");
1069 return 0;
1070}
1071
1072static void scsiPrintTemp(int device)
1073{
1074 UINT8 temp = 0;
1075 UINT8 trip = 0;
1076
1077 if (scsiGetTemp(device, &temp, &trip))
1078 return;
1079
1080 if (temp) {
1081 if (255 != temp)
1082 pout("Current Drive Temperature: %d C\n", temp);
1083 else
1084 pout("Current Drive Temperature: <not available>\n");
1085 }
1086 if (trip)
1087 pout("Drive Trip Temperature: %d C\n", trip);
1088}
1089
1090/* Main entry point used by smartctl command. Return 0 for success */
1091int scsiPrintMain(int fd)
1092{
1093 int checkedSupportedLogPages = 0;
1094 UINT8 peripheral_type = 0;
1095 int returnval = 0;
1096 int res, durationSec;
1097
1098 res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
1099 if (res) {
1100 if (2 == res)
1101 return 0;
1102 else
1103 failuretest(MANDATORY_CMD, returnval |= FAILID);
1104 }
1105
1106 if (con->smartenable) {
1107 if (scsiSmartEnable(fd))
1108 failuretest(MANDATORY_CMD, returnval |= FAILSMART);
1109 }
1110
1111 if (con->smartdisable) {
1112 if (scsiSmartDisable(fd))
1113 failuretest(MANDATORY_CMD,returnval |= FAILSMART);
1114 }
1115
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);
1120 }
1121 }
1122
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);
1127 }
1128 }
1129
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) {
1136 if (con->driveinfo)
1137 pout("TapeAlert Supported\n");
1138 if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
1139 failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
1140 }
1141 else
1142 pout("TapeAlert Not Supported\n");
1143 } else { /* disk, cd/dvd, enclosure, etc */
1144 if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) {
1145 if (-2 == res)
1146 returnval |= FAILSTATUS;
1147 else
1148 returnval |= FAILSMART;
1149 }
1150 }
1151 }
1152 if (con->smartvendorattrib) {
1153 if (! checkedSupportedLogPages)
1154 scsiGetSupportedLogPages(fd);
1155 if (gTempLPage) {
1156 if (con->checksmart)
1157 pout("\n");
1158 scsiPrintTemp(fd);
1159 }
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);
1168 }
1169 }
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");
1177 }
1178 if (con->smartselftestlog) {
1179 if (! checkedSupportedLogPages)
1180 scsiGetSupportedLogPages(fd);
1181 res = 0;
1182 if (gSelfTestLPage)
1183 res = scsiPrintSelfTest(fd);
1184 else {
1185 pout("Device does not support Self Test logging\n");
1186 failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
1187 }
1188 if (0 != res)
1189 failuretest(OPTIONAL_CMD, returnval|=res);
1190 }
1191 if (con->smartexeoffimmediate) {
1192 if (scsiSmartDefaultSelfTest(fd))
1193 return returnval | FAILSMART;
1194 pout("Default Self Test Successful\n");
1195 }
1196 if (con->smartshortcapselftest) {
1197 if (scsiSmartShortCapSelfTest(fd))
1198 return returnval | FAILSMART;
1199 pout("Short Foreground Self Test Successful\n");
1200 }
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");
1206 }
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);
1214
1215 t += durationSec;
1216 pout("Please wait %d minutes for test to complete.\n",
1217 durationSec / 60);
1218 pout("Estimated completion time: %s\n", ctime(&t));
1219 }
1220 pout("Use smartctl -X to abort test\n");
1221 }
1222 if (con->smartextendcapselftest) {
1223 if (scsiSmartExtendCapSelfTest(fd))
1224 return returnval | FAILSMART;
1225 pout("Extended Foreground Self Test Successful\n");
1226 }
1227 if (con->smartselftestabort) {
1228 if (scsiSmartSelfTestAbort(fd))
1229 return returnval | FAILSMART;
1230 pout("Self Test returned without error\n");
1231 }
1232 return returnval;
1233}