]>
Commit | Line | Data |
---|---|---|
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 | ||
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; | |
46 | ||
47 | // control block which points to external global control variables | |
48 | extern smartmonctrl *con; | |
49 | ||
50 | // to hold onto exit code for atexit routine | |
51 | extern int exitstatus; | |
52 | ||
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 | |
57 | ||
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; | |
71 | ||
72 | /* Mode pages supported */ | |
73 | static int gIecMPage = 1; /* N.B. assume it until we know otherwise */ | |
74 | ||
75 | /* Remember last successful mode sense/select command */ | |
76 | static int modese_len = 0; | |
77 | ||
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); | |
81 | ||
82 | static 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). */ | |
141 | static 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 | ¤ttemp, &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 | |
185 | static char *severities = "CWI"; | |
186 | ||
187 | static 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 | ||
235 | static 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 | ||
278 | static 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 | ||
327 | static 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 | ||
402 | static 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 | ||
496 | static 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 | ||
601 | static 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 | ||
612 | static 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. | |
635 | static 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 | ||
780 | static 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 | ||
799 | static 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 */ | |
819 | static 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 | ||
1000 | static 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 | ||
1036 | static 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 | ||
1072 | static 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 */ | |
1091 | int 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 | } |