]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - scsiprint.cpp
Merge branch 'cvs' into cvs-merge
[mirror_smartmontools-debian.git] / scsiprint.cpp
1 /*
2 * scsiprint.cpp
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
6 * Copyright (C) 2002-7 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-7 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 #include "scsiata.h"
42
43 #define GBUF_SIZE 65535
44
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;
47
48 // control block which points to external global control variables
49 extern smartmonctrl *con;
50
51 // to hold onto exit code for atexit routine
52 extern int exitstatus;
53
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
58
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;
73
74 /* Mode pages supported */
75 static int gIecMPage = 1; /* N.B. assume it until we know otherwise */
76
77 /* Remember last successful mode sense/select command */
78 static int modese_len = 0;
79
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);
83
84 static void scsiGetSupportedLogPages(int device)
85 {
86 int i, err;
87
88 if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
89 LOG_RESP_LEN, 0))) {
90 if (con->reportscsiioctl > 0)
91 pout("Log Sense for supported pages failed [%s]\n",
92 scsiErrString(err));
93 return;
94 }
95
96 for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
97 switch (gBuf[i])
98 {
99 case READ_ERROR_COUNTER_LPAGE:
100 gReadECounterLPage = 1;
101 break;
102 case WRITE_ERROR_COUNTER_LPAGE:
103 gWriteECounterLPage = 1;
104 break;
105 case VERIFY_ERROR_COUNTER_LPAGE:
106 gVerifyECounterLPage = 1;
107 break;
108 case LAST_N_ERROR_LPAGE:
109 gLastNErrorLPage = 1;
110 break;
111 case NON_MEDIUM_ERROR_LPAGE:
112 gNonMediumELPage = 1;
113 break;
114 case TEMPERATURE_LPAGE:
115 gTempLPage = 1;
116 break;
117 case STARTSTOP_CYCLE_COUNTER_LPAGE:
118 gStartStopLPage = 1;
119 break;
120 case SELFTEST_RESULTS_LPAGE:
121 gSelfTestLPage = 1;
122 break;
123 case IE_LPAGE:
124 gSmartLPage = 1;
125 break;
126 case BACKGROUND_RESULTS_LPAGE:
127 gBackgroundResultsLPage = 1;
128 break;
129 case TAPE_ALERTS_LPAGE:
130 gTapeAlertsLPage = 1;
131 break;
132 case SEAGATE_CACHE_LPAGE:
133 gSeagateCacheLPage = 1;
134 break;
135 case SEAGATE_FACTORY_LPAGE:
136 gSeagateFactoryLPage = 1;
137 break;
138 default:
139 break;
140 }
141 }
142 }
143
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)
147 {
148 UINT8 asc;
149 UINT8 ascq;
150 UINT8 currenttemp = 0;
151 UINT8 triptemp = 0;
152 const char * cp;
153 int err = 0;
154
155 PRINT_ON(con);
156 if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
157 &currenttemp, &triptemp)) {
158 /* error message already announced */
159 PRINT_OFF(con);
160 return -1;
161 }
162 PRINT_OFF(con);
163 cp = scsiGetIEString(asc, ascq);
164 if (cp) {
165 err = -2;
166 PRINT_ON(con);
167 pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
168 PRINT_OFF(con);
169 } else if (gIecMPage)
170 pout("SMART Health Status: OK\n");
171
172 if (attribs && !gTempLPage) {
173 if (currenttemp || triptemp)
174 pout("\n");
175 if (currenttemp) {
176 if (255 != currenttemp)
177 pout("Current Drive Temperature: %d C\n", currenttemp);
178 else
179 pout("Current Drive Temperature: <not available>\n");
180 }
181 if (triptemp)
182 pout("Drive Trip Temperature: %d C\n", triptemp);
183 }
184 return err;
185 }
186
187
188 // Returns number of logged errors or zero if none or -1 if fetching
189 // TapeAlerts fails
190 static char *severities = "CWI";
191
192 static int scsiGetTapeAlertsData(int device, int peripheral_type)
193 {
194 unsigned short pagelength;
195 unsigned short parametercode;
196 int i, err;
197 char *s;
198 const char *ts;
199 int failures = 0;
200
201 PRINT_ON(con);
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));
205 PRINT_OFF(con);
206 return -1;
207 }
208 if (gBuf[0] != 0x2e) {
209 pout("TapeAlerts Log Sense Failed\n");
210 PRINT_OFF(con);
211 return -1;
212 }
213 pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
214
215 for (s=severities; *s; s++) {
216 for (i = 4; i < pagelength; i += 5) {
217 parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
218
219 if (gBuf[i + 4]) {
220 ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
221 scsiTapeAlertsChangerDevice(parametercode) :
222 scsiTapeAlertsTapeDevice(parametercode);
223 if (*ts == *s) {
224 if (!failures)
225 pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
226 pout("[0x%02x] %s\n", parametercode, ts);
227 failures += 1;
228 }
229 }
230 }
231 }
232 PRINT_OFF(con);
233
234 if (! failures)
235 pout("TapeAlert: OK\n");
236
237 return failures;
238 }
239
240 static void scsiGetStartStopData(int device)
241 {
242 UINT32 u;
243 int err, len, k, extra, pc;
244 unsigned char * ucp;
245
246 if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
247 LOG_RESP_LEN, 0))) {
248 PRINT_ON(con);
249 pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
250 PRINT_OFF(con);
251 return;
252 }
253 if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
254 PRINT_ON(con);
255 pout("StartStop Log Sense Failed, page mismatch\n");
256 PRINT_OFF(con);
257 return;
258 }
259 len = ((gBuf[2] << 8) | gBuf[3]);
260 ucp = gBuf + 4;
261 for (k = len; k > 0; k -= extra, ucp += extra) {
262 if (k < 3) {
263 PRINT_ON(con);
264 pout("StartStop Log Sense Failed: short\n");
265 PRINT_OFF(con);
266 return;
267 }
268 extra = ucp[3] + 4;
269 pc = (ucp[0] << 8) + ucp[1];
270 switch (pc) {
271 case 1:
272 if (10 == extra)
273 pout("Manufactured in week %.2s of year %.4s\n", ucp + 8,
274 ucp + 4);
275 break;
276 case 2:
277 /* ignore Accounting date */
278 break;
279 case 3:
280 if (extra > 7) {
281 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
282 if (0xffffffff != u)
283 pout("Recommended maximum start stop count: %u times\n",
284 u);
285 }
286 break;
287 case 4:
288 if (extra > 7) {
289 u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
290 if (0xffffffff != u)
291 pout("Current start stop count: %u times\n", u);
292 }
293 break;
294 default:
295 /* ignore */
296 break;
297 }
298 }
299 }
300
301 static void scsiPrintGrownDefectListLen(int device)
302 {
303 int err, dl_format, dl_len, div;
304
305 memset(gBuf, 0, 4);
306 if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
307 4 /* bytes from index */, gBuf, 4))) {
308 if (con->reportscsiioctl > 0) {
309 PRINT_ON(con);
310 pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
311 PRINT_OFF(con);
312 }
313 return;
314 }
315 if (0x8 != (gBuf[1] & 0x18)) {
316 PRINT_ON(con);
317 pout("Read defect list: asked for grown list but didn't get it\n");
318 PRINT_OFF(con);
319 return;
320 }
321 div = 0;
322 dl_format = (gBuf[1] & 0x7);
323 switch (dl_format) {
324 case 0: /* short block */
325 div = 4;
326 break;
327 case 3: /* long block */
328 case 4: /* bytes from index */
329 case 5: /* physical sector */
330 div = 8;
331 break;
332 default:
333 PRINT_ON(con);
334 pout("defect list format %d unknown\n", dl_format);
335 PRINT_OFF(con);
336 break;
337 }
338 dl_len = (gBuf[2] << 8) + gBuf[3];
339 if (0 == dl_len)
340 pout("Elements in grown defect list: 0\n");
341 else {
342 if (0 == div)
343 pout("Grown defect list length=%d bytes [unknown "
344 "number of elements]\n", dl_len);
345 else
346 pout("Elements in grown defect list: %d\n", dl_len / div);
347 }
348 }
349
350 static void scsiPrintSeagateCacheLPage(int device)
351 {
352 int k, j, num, pl, pc, err, len;
353 unsigned char * ucp;
354 unsigned char * xp;
355 uint64_t ull;
356
357 if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
358 LOG_RESP_LEN, 0))) {
359 PRINT_ON(con);
360 pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
361 PRINT_OFF(con);
362 return;
363 }
364 if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
365 PRINT_ON(con);
366 pout("Seagate Cache Log Sense Failed, page mismatch\n");
367 PRINT_OFF(con);
368 return;
369 }
370 len = ((gBuf[2] << 8) | gBuf[3]) + 4;
371 num = len - 4;
372 ucp = &gBuf[0] + 4;
373 while (num > 3) {
374 pc = (ucp[0] << 8) | ucp[1];
375 pl = ucp[3] + 4;
376 switch (pc) {
377 case 0: case 1: case 2: case 3: case 4:
378 break;
379 default:
380 if (con->reportscsiioctl > 0) {
381 PRINT_ON(con);
382 pout("Vendor (Seagate) cache lpage has unexpected parameter"
383 ", skip\n");
384 PRINT_OFF(con);
385 }
386 return;
387 }
388 num -= pl;
389 ucp += pl;
390 }
391 pout("Vendor (Seagate) cache information\n");
392 num = len - 4;
393 ucp = &gBuf[0] + 4;
394 while (num > 3) {
395 pc = (ucp[0] << 8) | ucp[1];
396 pl = ucp[3] + 4;
397 switch (pc) {
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;
406 }
407 k = pl - 4;
408 xp = ucp + 4;
409 if (k > (int)sizeof(ull)) {
410 xp += (k - (int)sizeof(ull));
411 k = (int)sizeof(ull);
412 }
413 ull = 0;
414 for (j = 0; j < k; ++j) {
415 if (j > 0)
416 ull <<= 8;
417 ull |= xp[j];
418 }
419 pout(" = %"PRIu64"\n", ull);
420 num -= pl;
421 ucp += pl;
422 }
423 }
424
425 static void scsiPrintSeagateFactoryLPage(int device)
426 {
427 int k, j, num, pl, pc, len, err, good, bad;
428 unsigned char * ucp;
429 unsigned char * xp;
430 uint64_t ull;
431
432 if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
433 LOG_RESP_LEN, 0))) {
434 PRINT_ON(con);
435 pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
436 PRINT_OFF(con);
437 return;
438 }
439 if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
440 PRINT_ON(con);
441 pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
442 PRINT_OFF(con);
443 return;
444 }
445 len = ((gBuf[2] << 8) | gBuf[3]) + 4;
446 num = len - 4;
447 ucp = &gBuf[0] + 4;
448 good = 0;
449 bad = 0;
450 while (num > 3) {
451 pc = (ucp[0] << 8) | ucp[1];
452 pl = ucp[3] + 4;
453 switch (pc) {
454 case 0: case 8:
455 ++good;
456 break;
457 default:
458 ++bad;
459 break;
460 }
461 num -= pl;
462 ucp += pl;
463 }
464 if ((good < 2) || (bad > 4)) { /* heuristic */
465 if (con->reportscsiioctl > 0) {
466 PRINT_ON(con);
467 pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
468 "unexpected parameters, skip\n");
469 PRINT_OFF(con);
470 }
471 return;
472 }
473 pout("Vendor (Seagate/Hitachi) factory information\n");
474 num = len - 4;
475 ucp = &gBuf[0] + 4;
476 while (num > 3) {
477 pc = (ucp[0] << 8) | ucp[1];
478 pl = ucp[3] + 4;
479 good = 0;
480 switch (pc) {
481 case 0: pout(" number of hours powered up");
482 good = 1;
483 break;
484 case 8: pout(" number of minutes until next internal SMART test");
485 good = 1;
486 break;
487 default:
488 if (con->reportscsiioctl > 0) {
489 PRINT_ON(con);
490 pout("Vendor (Seagate/Hitachi) factory lpage: "
491 "unknown parameter code [0x%x]\n", pc);
492 PRINT_OFF(con);
493 }
494 break;
495 }
496 if (good) {
497 k = pl - 4;
498 xp = ucp + 4;
499 if (k > (int)sizeof(ull)) {
500 xp += (k - (int)sizeof(ull));
501 k = (int)sizeof(ull);
502 }
503 ull = 0;
504 for (j = 0; j < k; ++j) {
505 if (j > 0)
506 ull <<= 8;
507 ull |= xp[j];
508 }
509 if (0 == pc)
510 pout(" = %.2f\n", uint64_to_double(ull) / 60.0 );
511 else
512 pout(" = %"PRIu64"\n", ull);
513 }
514 num -= pl;
515 ucp += pl;
516 }
517 }
518
519 static void scsiPrintErrorCounterLog(int device)
520 {
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: "};
526 int k;
527 double processed_gb;
528
529 if (gReadECounterLPage && (0 == scsiLogSense(device,
530 READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
531 scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
532 found[0] = 1;
533 }
534 if (gWriteECounterLPage && (0 == scsiLogSense(device,
535 WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
536 scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
537 found[1] = 1;
538 }
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]) {
545 found[2] = 1;
546 break;
547 }
548 }
549 }
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) {
559 if (! found[k])
560 continue;
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]);
567 }
568 }
569 else
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);
574 if (nme.gotPC0)
575 pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0);
576 if (nme.gotTFE_H)
577 pout("Track following error count [Hitachi]: %8"PRIu64"\n",
578 nme.counterTFE_H);
579 if (nme.gotPE_H)
580 pout("Positioning error count [Hitachi]: %8"PRIu64"\n",
581 nme.counterPE_H);
582 }
583 if (gLastNErrorLPage && (0 == scsiLogSense(device,
584 LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) {
585 unsigned char * ucp;
586 int num, k, pc, pl, truncated;
587
588 num = (gBuf[2] << 8) + gBuf[3] + 4;
589 truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
590 if (truncated)
591 num = LOG_RESP_LONG_LEN;
592 ucp = gBuf + 4;
593 num -= 4;
594 if (num < 4)
595 pout("\nNo error events logged\n");
596 else {
597 pout("\nLast n error events log page\n");
598 for (k = num; k > 0; k -= pl, ucp += pl) {
599 if (k < 3) {
600 pout(" <<short Last n error events log page>>\n");
601 break;
602 }
603 pl = ucp[3] + 4;
604 pc = (ucp[0] << 8) + ucp[1];
605 if (pl > 4) {
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));
613 } else {
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);
618 }
619 }
620 }
621 }
622 if (truncated)
623 pout(" >>>> log truncated, fetched %d of %d available "
624 "bytes\n", LOG_RESP_LONG_LEN, truncated);
625 }
626 }
627 }
628
629 static const char * self_test_code[] = {
630 "Default ",
631 "Background short",
632 "Background long ",
633 "Reserved(3) ",
634 "Abort background",
635 "Foreground short",
636 "Foreground long ",
637 "Reserved(7) "
638 };
639
640 static const char * self_test_result[] = {
641 "Completed ",
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 --> ",
649 "Reserved(8) ",
650 "Reserved(9) ",
651 "Reserved(10) ",
652 "Reserved(11) ",
653 "Reserved(12) ",
654 "Reserved(13) ",
655 "Reserved(14) ",
656 "Self test in progress ..."
657 };
658
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)
664 {
665 int num, k, n, res, err, durationSec;
666 int noheader = 1;
667 int retval = 0;
668 UINT8 * ucp;
669 uint64_t ull=0;
670
671 if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
672 LOG_RESP_SELF_TEST_LEN, 0))) {
673 PRINT_ON(con);
674 pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
675 PRINT_OFF(con);
676 return FAILSMART;
677 }
678 if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
679 PRINT_ON(con);
680 pout("Self-test Log Sense Failed, page mismatch\n");
681 PRINT_OFF(con);
682 return FAILSMART;
683 }
684 // compute page length
685 num = (gBuf[2] << 8) + gBuf[3];
686 // Log sense page length 0x190 bytes
687 if (num != 0x190) {
688 PRINT_ON(con);
689 pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
690 PRINT_OFF(con);
691 return FAILSMART;
692 }
693 // loop through the twenty possible entries
694 for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
695 int i;
696
697 // timestamp in power-on hours (or zero if test in progress)
698 n = (ucp[6] << 8) | ucp[7];
699
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]))
703 break;
704
705 // only print header if needed
706 if (noheader) {
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 "
711 "(hours)\n");
712 noheader=0;
713 }
714
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]);
718
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)) {
722 case 0x3:
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
726 retval|=FAILSMART;
727 break;
728 case 0x4:
729 // the self-test completed with a failure in a test
730 // segment, and the test segment that failed is not
731 // known
732 retval|=FAILLOG;
733 break;
734 case 0x5:
735 // the first segment of the self-test failed
736 retval|=FAILLOG;
737 break;
738 case 0x6:
739 // the second segment of the self-test failed
740 retval|=FAILLOG;
741 break;
742 case 0x7:
743 // another segment of the self-test failed and which
744 // test is indicated by the contents of the SELF-TEST
745 // NUMBER field
746 retval|=FAILLOG;
747 break;
748 default:
749 break;
750 }
751 pout(" %s", self_test_result[res]);
752
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
758 // single byte.
759 if (ucp[5])
760 pout(" %3d", (int)ucp[5]);
761 else
762 pout(" -");
763
764 // print time that the self-test was completed
765 if (n==0 && res==0xf)
766 // self-test in progress
767 pout(" NOW");
768 else
769 pout(" %5d", n);
770
771 // construct 8-byte integer address of first failure
772 for (i = 0; i < 8; i++) {
773 ull <<= 8;
774 ull |= ucp[i+8];
775 }
776 // print Address of First Failure, if sensible
777 if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
778 char buff[32];
779
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);
783 pout("%18s", buff);
784 } else
785 pout(" -");
786
787 // if sense key nonzero, then print it, along with
788 // additional sense code and additional sense code qualifier
789 if (ucp[16] & 0xf)
790 pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
791 else
792 pout(" [- - -]\n");
793 }
794
795 // if header never printed, then there was no output
796 if (noheader)
797 pout("No self-tests have been logged\n");
798 else
799 pout("\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);
804 }
805 return retval;
806 }
807
808 static const char * bms_status[] = {
809 "no scans active",
810 "scan is active",
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 */
818 };
819
820 static const char * reassign_status[] = {
821 "No reassignment needed",
822 "Require Reassign or Write command",
823 "Successfully reassigned",
824 "Reserved [0x3]",
825 "Failed",
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 */
830 };
831
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)
837 {
838 int num, j, m, err, pc, pl, truncated;
839 int noheader = 1;
840 int firstresult = 1;
841 int retval = 0;
842 UINT8 * ucp;
843
844 if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
845 LOG_RESP_LONG_LEN, 0))) {
846 PRINT_ON(con);
847 pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err));
848 PRINT_OFF(con);
849 return FAILSMART;
850 }
851 if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
852 PRINT_ON(con);
853 pout("Background scan results Log Sense Failed, page mismatch\n");
854 PRINT_OFF(con);
855 return FAILSMART;
856 }
857 // compute page length
858 num = (gBuf[2] << 8) + gBuf[3] + 4;
859 if (num < 20) {
860 PRINT_ON(con);
861 pout("Background scan results Log Sense length is %d, no scan "
862 "status\n", num);
863 PRINT_OFF(con);
864 return FAILSMART;
865 }
866 truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
867 if (truncated)
868 num = LOG_RESP_LONG_LEN;
869 ucp = gBuf + 4;
870 num -= 4;
871 while (num > 3) {
872 pc = (ucp[0] << 8) | ucp[1];
873 // pcb = ucp[2];
874 pl = ucp[3] + 4;
875 switch (pc) {
876 case 0:
877 if (noheader) {
878 noheader = 0;
879 pout("\nBackground scan results log\n");
880 }
881 pout(" Status: ");
882 if ((pl < 16) || (num < 16)) {
883 pout("\n");
884 break;
885 }
886 j = ucp[9];
887 if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
888 pout("%s\n", bms_status[j]);
889 else
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);
898 break;
899 default:
900 if (noheader) {
901 noheader = 0;
902 pout("\nBackground scan results log\n");
903 }
904 if (firstresult) {
905 firstresult = 0;
906 pout("\n # when lba(hex) [sk,asc,ascq] "
907 "reassign_status\n");
908 }
909 pout(" %3d ", pc);
910 if ((pl < 24) || (num < 24)) {
911 if (pl < 24)
912 pout("parameter length >= 24 expected, got %d\n", pl);
913 break;
914 }
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;
921 if (j <
922 (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
923 pout("%s\n", reassign_status[j]);
924 else
925 pout("Reassign status: reserved [0x%x]\n", j);
926 break;
927 }
928 num -= pl;
929 ucp += pl;
930 }
931 if (truncated)
932 pout(" >>>> log truncated, fetched %d of %d available "
933 "bytes\n", LOG_RESP_LONG_LEN, truncated);
934 return retval;
935 }
936
937 static const char * peripheral_dt_arr[] = {
938 "disk",
939 "tape",
940 "printer",
941 "processor",
942 "optical disk(4)",
943 "CD/DVD",
944 "scanner",
945 "optical disk(7)",
946 "medium changer",
947 "communications",
948 "graphics(10)",
949 "graphics(11)",
950 "storage array",
951 "enclosure",
952 "simplified disk",
953 "optical card reader"
954 };
955
956 static const char * transport_proto_arr[] = {
957 "Fibre channel (FCP-2)",
958 "Parallel SCSI (SPI-4)",
959 "SSA",
960 "IEEE 1394 (SBP-2)",
961 "RDMA (SRP)",
962 "iSCSI",
963 "SAS",
964 "ADT",
965 "0x8",
966 "0x9",
967 "0xa",
968 "0xb",
969 "0xc",
970 "0xd",
971 "0xe",
972 "0xf"
973 };
974
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)
977 {
978 char manufacturer[9];
979 char product[17];
980 char revision[5];
981 char timedatetz[DATEANDEPOCHLEN];
982 struct scsi_iec_mode_page iec;
983 int err, iec_err, len, req_len, avail_len, val;
984 int is_tape = 0;
985 int peri_dt = 0;
986 int returnval=0;
987
988 memset(gBuf, 0, 96);
989 req_len = 36;
990 if ((err = scsiStdInquiry(device, gBuf, req_len))) {
991 PRINT_ON(con);
992 pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
993 pout("Retrying with a 64 byte Standard Inquiry\n");
994 PRINT_OFF(con);
995 /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
996 req_len = 64;
997 if ((err = scsiStdInquiry(device, gBuf, req_len))) {
998 PRINT_ON(con);
999 pout("Standard Inquiry (64 bytes) failed [%s]\n",
1000 scsiErrString(err));
1001 PRINT_OFF(con);
1002 return 1;
1003 }
1004 }
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;
1010
1011 if (len < 36) {
1012 PRINT_ON(con);
1013 pout("Short INQUIRY response, skip product id\n");
1014 PRINT_OFF(con);
1015 return 1;
1016 }
1017 memset(manufacturer, 0, sizeof(manufacturer));
1018 strncpy(manufacturer, (char *)&gBuf[8], 8);
1019
1020 memset(product, 0, sizeof(product));
1021 strncpy(product, (char *)&gBuf[16], 16);
1022
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);
1027
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");
1031 #else
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");
1034 #endif
1035 return 2;
1036 } else if ((len >= 42) &&
1037 (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) {
1038 pout("please try '-d marvell'\n");
1039 return 2;
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) {
1045 PRINT_ON(con);
1046 pout("Detected SAT interface, switch to device type 'sat'\n");
1047 PRINT_OFF(con);
1048 }
1049 return 2;
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");
1054 return 2;
1055 }
1056 if (! all)
1057 return 0;
1058
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 "
1064 "mode page\n");
1065 PRINT_OFF(con);
1066 gIecMPage = 0;
1067 return 1;
1068 }
1069 } else
1070 modese_len = iec.modese_len;
1071
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 */
1076 len = gBuf[3];
1077 gBuf[4 + len] = '\0';
1078 pout("Serial number: %s\n", &gBuf[4]);
1079 }
1080 else if (con->reportscsiioctl > 0) {
1081 PRINT_ON(con);
1082 if (SIMPLE_ERR_BAD_RESP == err)
1083 pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
1084 else
1085 pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
1086 PRINT_OFF(con);
1087 }
1088 }
1089
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]);
1094 else
1095 pout("Device type: <%d>\n", peri_dt);
1096
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]);
1101
1102 // print current time and date and timezone
1103 dateandtimezone(timedatetz);
1104 pout("Local Time is: %s\n", timedatetz);
1105
1106 if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
1107 (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
1108 is_tape = 1;
1109 // See if unit accepts SCSI commmands from us
1110 if ((err = scsiTestUnitReady(device))) {
1111 if (SIMPLE_ERR_NOT_READY == err) {
1112 PRINT_ON(con);
1113 if (!is_tape)
1114 pout("device is NOT READY (e.g. spun down, busy)\n");
1115 else
1116 pout("device is NOT READY (e.g. no tape)\n");
1117 PRINT_OFF(con);
1118 } else if (SIMPLE_ERR_NO_MEDIUM == err) {
1119 PRINT_ON(con);
1120 pout("NO MEDIUM present on device\n");
1121 PRINT_OFF(con);
1122 } else if (SIMPLE_ERR_BECOMING_READY == err) {
1123 PRINT_ON(con);
1124 pout("device becoming ready (wait)\n");
1125 PRINT_OFF(con);
1126 } else {
1127 PRINT_ON(con);
1128 pout("device Test Unit Ready [%s]\n", scsiErrString(err));
1129 PRINT_OFF(con);
1130 }
1131 failuretest(MANDATORY_CMD, returnval|=FAILID);
1132 }
1133
1134 if (iec_err) {
1135 if (!is_tape) {
1136 PRINT_ON(con);
1137 pout("Device does not support SMART");
1138 if (con->reportscsiioctl > 0)
1139 pout(" [%s]\n", scsiErrString(iec_err));
1140 else
1141 pout("\n");
1142 PRINT_OFF(con);
1143 }
1144 gIecMPage = 0;
1145 return 0;
1146 }
1147
1148 if (!is_tape)
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");
1154 return 0;
1155 }
1156
1157 static int scsiSmartEnable(int device)
1158 {
1159 struct scsi_iec_mode_page iec;
1160 int err;
1161
1162 if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
1163 PRINT_ON(con);
1164 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1165 scsiErrString(err));
1166 PRINT_OFF(con);
1167 return 1;
1168 } else
1169 modese_len = iec.modese_len;
1170
1171 if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
1172 PRINT_ON(con);
1173 pout("unable to enable Exception control and warning [%s]\n",
1174 scsiErrString(err));
1175 PRINT_OFF(con);
1176 return 1;
1177 }
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));
1182 return 1;
1183 } else
1184 modese_len = iec.modese_len;
1185
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");
1190 return 0;
1191 }
1192
1193 static int scsiSmartDisable(int device)
1194 {
1195 struct scsi_iec_mode_page iec;
1196 int err;
1197
1198 if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
1199 PRINT_ON(con);
1200 pout("unable to fetch IEC (SMART) mode page [%s]\n",
1201 scsiErrString(err));
1202 PRINT_OFF(con);
1203 return 1;
1204 } else
1205 modese_len = iec.modese_len;
1206
1207 if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
1208 PRINT_ON(con);
1209 pout("unable to disable Exception control and warning [%s]\n",
1210 scsiErrString(err));
1211 PRINT_OFF(con);
1212 return 1;
1213 }
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));
1218 return 1;
1219 } else
1220 modese_len = iec.modese_len;
1221
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");
1226 return 0;
1227 }
1228
1229 static void scsiPrintTemp(int device)
1230 {
1231 UINT8 temp = 0;
1232 UINT8 trip = 0;
1233
1234 if (scsiGetTemp(device, &temp, &trip))
1235 return;
1236
1237 if (temp) {
1238 if (255 != temp)
1239 pout("Current Drive Temperature: %d C\n", temp);
1240 else
1241 pout("Current Drive Temperature: <not available>\n");
1242 }
1243 if (trip)
1244 pout("Drive Trip Temperature: %d C\n", trip);
1245 }
1246
1247 /* Main entry point used by smartctl command. Return 0 for success */
1248 int scsiPrintMain(int fd)
1249 {
1250 int checkedSupportedLogPages = 0;
1251 UINT8 peripheral_type = 0;
1252 int returnval = 0;
1253 int res, durationSec;
1254
1255 res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
1256 if (res) {
1257 if (2 == res)
1258 return 0;
1259 else
1260 failuretest(MANDATORY_CMD, returnval |= FAILID);
1261 }
1262
1263 if (con->smartenable) {
1264 if (scsiSmartEnable(fd))
1265 failuretest(MANDATORY_CMD, returnval |= FAILSMART);
1266 }
1267
1268 if (con->smartdisable) {
1269 if (scsiSmartDisable(fd))
1270 failuretest(MANDATORY_CMD,returnval |= FAILSMART);
1271 }
1272
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);
1277 }
1278 }
1279
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);
1284 }
1285 }
1286
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) {
1293 if (con->driveinfo)
1294 pout("TapeAlert Supported\n");
1295 if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
1296 failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
1297 }
1298 else
1299 pout("TapeAlert Not Supported\n");
1300 } else { /* disk, cd/dvd, enclosure, etc */
1301 if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) {
1302 if (-2 == res)
1303 returnval |= FAILSTATUS;
1304 else
1305 returnval |= FAILSMART;
1306 }
1307 }
1308 }
1309 if (con->smartvendorattrib) {
1310 if (! checkedSupportedLogPages)
1311 scsiGetSupportedLogPages(fd);
1312 if (gTempLPage) {
1313 if (con->checksmart)
1314 pout("\n");
1315 scsiPrintTemp(fd);
1316 }
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);
1325 }
1326 }
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");
1334 }
1335 if (con->smartselftestlog) {
1336 if (! checkedSupportedLogPages)
1337 scsiGetSupportedLogPages(fd);
1338 res = 0;
1339 if (gSelfTestLPage)
1340 res = scsiPrintSelfTest(fd);
1341 else {
1342 pout("Device does not support Self Test logging\n");
1343 failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
1344 }
1345 if (0 != res)
1346 failuretest(OPTIONAL_CMD, returnval|=res);
1347 }
1348 if (con->smartbackgroundlog) {
1349 if (! checkedSupportedLogPages)
1350 scsiGetSupportedLogPages(fd);
1351 res = 0;
1352 if (gBackgroundResultsLPage)
1353 res = scsiPrintBackgroundResults(fd);
1354 else {
1355 pout("Device does not support Background scan results logging\n");
1356 failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
1357 }
1358 if (0 != res)
1359 failuretest(OPTIONAL_CMD, returnval|=res);
1360 }
1361 if (con->smartexeoffimmediate) {
1362 if (scsiSmartDefaultSelfTest(fd))
1363 return returnval | FAILSMART;
1364 pout("Default Self Test Successful\n");
1365 }
1366 if (con->smartshortcapselftest) {
1367 if (scsiSmartShortCapSelfTest(fd))
1368 return returnval | FAILSMART;
1369 pout("Short Foreground Self Test Successful\n");
1370 }
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");
1376 }
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);
1384
1385 t += durationSec;
1386 pout("Please wait %d minutes for test to complete.\n",
1387 durationSec / 60);
1388 pout("Estimated completion time: %s\n", ctime(&t));
1389 }
1390 pout("Use smartctl -X to abort test\n");
1391 }
1392 if (con->smartextendcapselftest) {
1393 if (scsiSmartExtendCapSelfTest(fd))
1394 return returnval | FAILSMART;
1395 pout("Extended Foreground Self Test Successful\n");
1396 }
1397 if (con->smartselftestabort) {
1398 if (scsiSmartSelfTestAbort(fd))
1399 return returnval | FAILSMART;
1400 pout("Self Test returned without error\n");
1401 }
1402 return returnval;
1403 }