]>
Commit | Line | Data |
---|---|---|
832b75ed | 1 | /* |
4d59bff9 | 2 | * scsiprint.cpp |
832b75ed GG |
3 | * |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
34ad0c5f | 6 | * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net> |
832b75ed GG |
7 | * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> |
8 | * | |
9 | * Additional SCSI work: | |
34ad0c5f | 10 | * Copyright (C) 2003-8 Douglas Gilbert <dougg@torque.net> |
832b75ed GG |
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" | |
4d59bff9 | 41 | #include "scsiata.h" |
832b75ed GG |
42 | |
43 | #define GBUF_SIZE 65535 | |
44 | ||
34ad0c5f | 45 | const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.121 2008/03/04 22:09:47 ballen4705 Exp $" |
832b75ed GG |
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 | |
4d59bff9 | 56 | #define LOG_RESP_LONG_LEN 16384 |
832b75ed GG |
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; | |
4d59bff9 | 69 | static int gBackgroundResultsLPage = 0; |
832b75ed GG |
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 | ||
4d59bff9 | 88 | if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf, |
832b75ed GG |
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; | |
4d59bff9 GG |
126 | case BACKGROUND_RESULTS_LPAGE: |
127 | gBackgroundResultsLPage = 1; | |
128 | break; | |
832b75ed GG |
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 | ¤ttemp, &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); | |
4d59bff9 | 202 | if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf, |
832b75ed GG |
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 | { | |
4d59bff9 GG |
242 | UINT32 u; |
243 | int err, len, k, extra, pc; | |
244 | unsigned char * ucp; | |
832b75ed | 245 | |
4d59bff9 | 246 | if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf, |
832b75ed GG |
247 | LOG_RESP_LEN, 0))) { |
248 | PRINT_ON(con); | |
249 | pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err)); | |
250 | PRINT_OFF(con); | |
251 | return; | |
252 | } | |
4d59bff9 | 253 | if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) { |
832b75ed GG |
254 | PRINT_ON(con); |
255 | pout("StartStop Log Sense Failed, page mismatch\n"); | |
256 | PRINT_OFF(con); | |
257 | return; | |
258 | } | |
4d59bff9 GG |
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 | } | |
832b75ed GG |
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 | ||
4d59bff9 | 357 | if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf, |
832b75ed GG |
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 | } | |
4d59bff9 | 364 | if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) { |
832b75ed GG |
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 | ||
4d59bff9 | 432 | if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf, |
832b75ed GG |
433 | LOG_RESP_LEN, 0))) { |
434 | PRINT_ON(con); | |
435 | pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err)); | |
436 | PRINT_OFF(con); | |
437 | return; | |
438 | } | |
4d59bff9 | 439 | if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) { |
832b75ed GG |
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, | |
4d59bff9 | 530 | READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { |
832b75ed GG |
531 | scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]); |
532 | found[0] = 1; | |
533 | } | |
534 | if (gWriteECounterLPage && (0 == scsiLogSense(device, | |
4d59bff9 | 535 | WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { |
832b75ed GG |
536 | scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]); |
537 | found[1] = 1; | |
538 | } | |
539 | if (gVerifyECounterLPage && (0 == scsiLogSense(device, | |
4d59bff9 | 540 | VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { |
832b75ed GG |
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, | |
4d59bff9 | 572 | NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) { |
832b75ed GG |
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, | |
4d59bff9 | 584 | LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) { |
832b75ed | 585 | unsigned char * ucp; |
4d59bff9 | 586 | int num, k, pc, pl, truncated; |
832b75ed GG |
587 | |
588 | num = (gBuf[2] << 8) + gBuf[3] + 4; | |
4d59bff9 GG |
589 | truncated = (num > LOG_RESP_LONG_LEN) ? num : 0; |
590 | if (truncated) | |
591 | num = LOG_RESP_LONG_LEN; | |
832b75ed GG |
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); | |
4d59bff9 | 618 | } |
832b75ed GG |
619 | } |
620 | } | |
621 | } | |
4d59bff9 GG |
622 | if (truncated) |
623 | pout(" >>>> log truncated, fetched %d of %d available " | |
624 | "bytes\n", LOG_RESP_LONG_LEN, truncated); | |
832b75ed GG |
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 | ||
4d59bff9 | 671 | if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf, |
832b75ed GG |
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 | } | |
4d59bff9 | 678 | if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) { |
832b75ed GG |
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 | ||
4d59bff9 GG |
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 | ||
832b75ed GG |
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; | |
832b75ed GG |
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); | |
4d59bff9 GG |
1025 | if (all && (0 != strncmp(manufacturer, "ATA", 3))) |
1026 | pout("Device: %s %s Version: %s\n", manufacturer, product, revision); | |
832b75ed GG |
1027 | |
1028 | if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) { | |
4d59bff9 GG |
1029 | #if defined(_WIN32) || defined(__CYGWIN__) |
1030 | pout("please try changing device to /dev/hdX,N\n"); | |
1031 | #else | |
832b75ed GG |
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"); | |
4d59bff9 | 1034 | #endif |
832b75ed GG |
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; | |
4d59bff9 GG |
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; | |
9ebc753d GG |
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; | |
832b75ed | 1055 | } |
4d59bff9 GG |
1056 | if (! all) |
1057 | return 0; | |
832b75ed GG |
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 | ||
a37e7145 GG |
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 | } | |
832b75ed GG |
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 | } | |
4d59bff9 GG |
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 | } | |
832b75ed GG |
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 | } |