ataprint.cpp 34.4 KB
Newer Older
ballen4705's avatar
ballen4705 committed
1
2
3
/*
 * ataprint.c
 *
4
5
 * Home page of code is: http://smartmontools.sourceforge.net
 *
ballen4705's avatar
ballen4705 committed
6
 * Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
ballen4705's avatar
ballen4705 committed
7
8
9
10
11
12
13
14
15
16
 * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * You should have received a copy of the GNU General Public License
 * (for example COPYING); if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
ballen4705's avatar
ballen4705 committed
17
18
19
20
21
22
 *
 * This code was originally developed as a Senior Thesis by Michael Cornwell
 * at the Concurrent Systems Laboratory (now part of the Storage Systems
 * Research Center), Jack Baskin School of Engineering, University of
 * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 *
ballen4705's avatar
ballen4705 committed
23
24
 */

25
26
#include <ctype.h>
#include <stdio.h>
ballen4705's avatar
ballen4705 committed
27
#include <syslog.h>
ballen4705's avatar
ballen4705 committed
28
#include <time.h>
29
#include "atacmds.h"
ballen4705's avatar
ballen4705 committed
30
31
32
33
#include "ataprint.h"
#include "smartctl.h"
#include "extern.h"

ballen4705's avatar
ballen4705 committed
34
const char *CVSid2="$Id: ataprint.cpp,v 1.53 2003/01/12 10:30:53 ballen4705 Exp $"
35
36
37
38
CVSID1 CVSID2 CVSID3 CVSID6;

// for passing global control variables
extern atamainctrl *con;
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Function for printing ASCII byte-swapped strings, skipping white
// space. This is needed on little-endian architectures, eg Intel,
// Alpha. If someone wants to run this on SPARC they'll need to test
// for the Endian-ness and skip the byte swapping if it's big-endian.
void printswap(char *in, unsigned int n){
  unsigned int i;
  char out[64];

  // swap bytes
  for (i=0;i<n;i+=2){
    unsigned int j=i+1;
    out[i]=in[j];
    out[j]=in[i];
  }

  // find the end of the white space
  for (i=0;i<n && isspace(out[i]);i++);

  // and do the printing starting from first non-white space
  if (n-i)
60
    pout("%.*s\n",(int)(n-i),out+i);
61
  else
62
    pout("[No Information Found]\n");
63
64
65
66
67

  return;
}


ballen4705's avatar
ballen4705 committed
68
void ataPrintDriveInfo (struct hd_driveid *drive){
69
70
  int version;
  const char *description;
71
  char unknown[64];
72
  unsigned short minorrev;
ballen4705's avatar
ballen4705 committed
73
  time_t tval;
74

75
  // print out model, serial # and firmware versions  (byte-swap ASCI strings)
76
  pout("Device Model:     ");
ballen4705's avatar
ballen4705 committed
77
  printswap(drive->model,40);
78

79
  pout("Serial Number:    ");
ballen4705's avatar
ballen4705 committed
80
  printswap(drive->serial_no,20);
81

82
  pout("Firmware Version: ");
ballen4705's avatar
ballen4705 committed
83
  printswap(drive->fw_rev,8);
84
85

  // now get ATA version info
86
  version=ataVersionInfo(&description,drive, &minorrev);
87
88
89

  // unrecognized minor revision code
  if (!description){
ballen4705's avatar
ballen4705 committed
90
    sprintf(unknown,"Unrecognized. Minor revision code: 0x%02hx",minorrev);
91
92
93
    description=unknown;
  }
  
94
95
96
97
98
99
100
  
  // SMART Support was first added into the ATA/ATAPI-3 Standard with
  // Revision 3 of the document, July 25, 1995.  Look at the "Document
  // Status" revision commands at the beginning of
  // http://www.t13.org/project/d2008r6.pdf to see this.  So it's not
  // enough to check if we are ATA-3.  Version=-3 indicates ATA-3
  // BEFORE Revision 3.
ballen4705's avatar
ballen4705 committed
101
  pout("ATA Version is:   %d\n",(int)abs(version));
102
  pout("ATA Standard is:  %s\n",description);
103
  
ballen4705's avatar
ballen4705 committed
104
105
  // print current time and date
  tval=time(NULL);
ballen4705's avatar
ballen4705 committed
106
  pout("Local Time is:    %s", ctime(&tval));
ballen4705's avatar
ballen4705 committed
107
  
108
109
  if (version>=3)
    return;
110
  
111
112
  pout("SMART is only available in ATA Version 3 Revision 3 or greater.\n");
  pout("We will try to proceed in spite of this.\n");
113
  return;
ballen4705's avatar
ballen4705 committed
114
115
116
}


ballen4705's avatar
ballen4705 committed
117
118
/*  prints verbose value Off-line data collection status byte */
void PrintSmartOfflineStatus(struct ata_smart_values *data){
119
120
  pout("Off-line data collection status: ");	
  
ballen4705's avatar
ballen4705 committed
121
  switch(data->offline_data_collection_status){
122
123
124
  case 0x00:
  case 0x80:
    pout("(0x%02x)\tOffline data collection activity was\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
125
	 (int)data->offline_data_collection_status);
126
127
128
129
130
    pout("never started.\n");
    break;
  case 0x01:
  case 0x81:
    pout("(0x%02x)\tReserved.\n",
ballen4705's avatar
ballen4705 committed
131
	 (int)data->offline_data_collection_status);
132
133
134
135
    break;
  case 0x02:
  case 0x82:
    pout("(0x%02x)\tOffline data collection activity \n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
136
	 (int)data->offline_data_collection_status);
137
138
139
140
141
    pout("completed without error.\n");
    break;
  case 0x03:
  case 0x83:
    pout("(0x%02x)\tReserved.\n",
ballen4705's avatar
ballen4705 committed
142
	 (int)data->offline_data_collection_status);
143
144
145
146
    break;
  case 0x04:
  case 0x84:
    pout("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
147
	 (int)data->offline_data_collection_status);
148
149
150
151
152
    pout("suspended by an interrupting command from host.\n");
    break;
  case 0x05:
  case 0x85:
    pout("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
153
	 (int)data->offline_data_collection_status);
154
155
156
157
158
    pout("aborted by an interrupting command from host.\n");
    break;
  case 0x06:
  case 0x86:
    pout("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
159
	 (int)data->offline_data_collection_status);
160
161
162
    pout("aborted by the device with a fatal error.\n");
    break;
  default:
ballen4705's avatar
ballen4705 committed
163
164
165
166
167
    if ( ((data->offline_data_collection_status >= 0x07) &&
	  (data->offline_data_collection_status <= 0x3f)) ||
	 ((data->offline_data_collection_status >= 0xc0) &&
	  (data->offline_data_collection_status <= 0xff)) )
      pout("(0x%02x)\tVendor Specific.\n",(int)data->offline_data_collection_status);
168
    else
ballen4705's avatar
ballen4705 committed
169
      pout("(0x%02x)\tReserved.\n",(int)data->offline_data_collection_status);
170
  }
ballen4705's avatar
ballen4705 committed
171
172
173
174
}



ballen4705's avatar
ballen4705 committed
175
void PrintSmartSelfExecStatus(struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
176
{
177
   pout("Self-test execution status:      ");
ballen4705's avatar
ballen4705 committed
178
   
ballen4705's avatar
ballen4705 committed
179
   switch (data->self_test_exec_status >> 4)
ballen4705's avatar
ballen4705 committed
180
181
   {
      case 0:
182
        pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
183
                (int)data->self_test_exec_status);
184
        pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
ballen4705's avatar
ballen4705 committed
185
186
        break;
       case 1:
187
         pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
188
                 (int)data->self_test_exec_status);
189
         pout("the host.\n");
ballen4705's avatar
ballen4705 committed
190
191
         break;
       case 2:
192
         pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
193
                 (int)data->self_test_exec_status);
194
         pout("by the host with a hard or soft reset.\n");
ballen4705's avatar
ballen4705 committed
195
196
         break;
       case 3:
197
          pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
198
                  (int)data->self_test_exec_status);
199
200
201
202
          pout("occurred while the device was executing\n\t\t\t\t\t");
          pout("its self-test routine and the device \n\t\t\t\t\t");
          pout("was unable to complete the self-test \n\t\t\t\t\t");
          pout("routine.\n");
ballen4705's avatar
ballen4705 committed
203
204
          break;
       case 4:
205
          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
206
                  (int)data->self_test_exec_status);
207
208
          pout("a test element that failed and the test\n\t\t\t\t\t");
          pout("element that failed is not known.\n");
ballen4705's avatar
ballen4705 committed
209
210
          break;
       case 5:
211
          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
212
                  (int)data->self_test_exec_status);
213
214
          pout("the electrical element of the test\n\t\t\t\t\t");
          pout("failed.\n");
ballen4705's avatar
ballen4705 committed
215
216
          break;
       case 6:
217
          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
218
                  (int)data->self_test_exec_status);
219
220
          pout("the servo (and/or seek) element of the \n\t\t\t\t\t");
          pout("test failed.\n");
ballen4705's avatar
ballen4705 committed
221
222
          break;
       case 7:
223
          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
224
                  (int)data->self_test_exec_status);
225
          pout("the read element of the test failed.\n");
ballen4705's avatar
ballen4705 committed
226
227
          break;
       case 15:
228
          pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
229
                  (int)data->self_test_exec_status);
230
          pout("%1d0%% of test remaining.\n", 
ballen4705's avatar
ballen4705 committed
231
                  (int)(data->self_test_exec_status & 0x0f));
ballen4705's avatar
ballen4705 committed
232
233
          break;
       default:
234
          pout("(%4d)\tReserved.\n",
ballen4705's avatar
ballen4705 committed
235
                  (int)data->self_test_exec_status);
ballen4705's avatar
ballen4705 committed
236
237
238
239
240
241
242
          break;
   }
	
}



ballen4705's avatar
ballen4705 committed
243
void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){
244
245
  pout("Total time to complete off-line \n");
  pout("data collection: \t\t (%4d) seconds.\n", 
ballen4705's avatar
ballen4705 committed
246
       (int)data->total_time_to_complete_off_line);
ballen4705's avatar
ballen4705 committed
247
248
249
250
}



ballen4705's avatar
ballen4705 committed
251
void PrintSmartOfflineCollectCap(struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
252
{
253
254
   pout("Offline data collection\n");
   pout("capabilities: \t\t\t (0x%02x) ",
ballen4705's avatar
ballen4705 committed
255
            (int)data->offline_data_collection_capability);
ballen4705's avatar
ballen4705 committed
256

ballen4705's avatar
ballen4705 committed
257
   if (data->offline_data_collection_capability == 0x00)
ballen4705's avatar
ballen4705 committed
258
   {
259
      pout("\tOff-line data collection not supported.\n");
ballen4705's avatar
ballen4705 committed
260
261
262
   } 
   else 
   {
263
      pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
264
265
              "SMART execute Offline immediate." :
              "No SMART execute Offline immediate.");
ballen4705's avatar
ballen4705 committed
266

267
      pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? 
268
269
              "Automatic timer ON/OFF support.":
              "No Automatic timer ON/OFF support.");
ballen4705's avatar
ballen4705 committed
270
		
271
      pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? 
272
273
              "Abort Offline collection upon new\n\t\t\t\t\tcommand.":
              "Suspend Offline collection upon new\n\t\t\t\t\tcommand.");
ballen4705's avatar
ballen4705 committed
274

275
      pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? 
276
277
              "Offline surface scan supported.":
              "No Offline surface scan supported.");
ballen4705's avatar
ballen4705 committed
278

279
      pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? 
280
281
              "Self-test supported.":
              "No Self-test supported.");
ballen4705's avatar
ballen4705 committed
282
283
284
285
286
    }
}



ballen4705's avatar
ballen4705 committed
287
void PrintSmartCapability ( struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
288
{
289
   pout("SMART capabilities:            ");
ballen4705's avatar
ballen4705 committed
290
   pout("(0x%04x)\t", (int)data->smart_capability);
ballen4705's avatar
ballen4705 committed
291
   
ballen4705's avatar
ballen4705 committed
292
   if (data->smart_capability == 0x00)
ballen4705's avatar
ballen4705 committed
293
   {
294
       pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
ballen4705's avatar
ballen4705 committed
295
296
297
298
   } 
   else 
   {
	
ballen4705's avatar
ballen4705 committed
299
      pout( "%s\n", (data->smart_capability & 0x01)? 
300
301
              "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.":
              "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode.");
ballen4705's avatar
ballen4705 committed
302
		
ballen4705's avatar
ballen4705 committed
303
      if ( data->smart_capability & 0x02 )
ballen4705's avatar
ballen4705 committed
304
      {
305
          pout("\t\t\t\t\tSupports SMART auto save timer.\n");
ballen4705's avatar
ballen4705 committed
306
307
308
309
310
311
      }
   }
}



ballen4705's avatar
ballen4705 committed
312
void PrintSmartErrorLogCapability ( struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
313
314
{

315
   pout("Error logging capability:       ");
ballen4705's avatar
ballen4705 committed
316
317
318
    
   if ( isSmartErrorLogCapable(data) )
   {
319
      pout(" (0x%02x)\tError logging supported.\n",
ballen4705's avatar
ballen4705 committed
320
               (int)data->errorlog_capability);
ballen4705's avatar
ballen4705 committed
321
322
   }
   else {
323
       pout(" (0x%02x)\tError logging NOT supported.\n",
ballen4705's avatar
ballen4705 committed
324
                (int)data->errorlog_capability);
ballen4705's avatar
ballen4705 committed
325
326
327
328
329
   }
}



ballen4705's avatar
ballen4705 committed
330
void PrintSmartShortSelfTestPollingTime (struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
331
332
333
{
   if ( isSupportSelfTest(data) )
   {
334
335
      pout("Short self-test routine \n");
      pout("recommended polling time: \t (%4d) minutes.\n", 
ballen4705's avatar
ballen4705 committed
336
               (int)data->short_test_completion_time);
ballen4705's avatar
ballen4705 committed
337
338
339
340

   }
   else
   {
341
342
      pout("Short self-test routine \n");
      pout("recommended polling time: \t        Not Supported.\n");
ballen4705's avatar
ballen4705 committed
343
344
345
346
   }
}


ballen4705's avatar
ballen4705 committed
347
void PrintSmartExtendedSelfTestPollingTime ( struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
348
349
350
{
   if ( isSupportSelfTest(data) )
   {
351
352
      pout("Extended self-test routine \n");
      pout("recommended polling time: \t (%4d) minutes.\n", 
ballen4705's avatar
ballen4705 committed
353
               (int)data->extend_test_completion_time);
ballen4705's avatar
ballen4705 committed
354
355
356
   }
   else
   {
357
358
      pout("Extended self-test routine \n");
      pout("recommended polling time: \t        Not Supported.\n");
ballen4705's avatar
ballen4705 committed
359
360
361
362
   }
}


363
364
365
// onlyfailed=0 : print all attribute values
// onlyfailed=1:  just ones that are currently failed and have prefailure bit set
// onlyfailed=2:  ones that are failed, or have failed with or without prefailure bit set
ballen4705's avatar
ballen4705 committed
366
367
void PrintSmartAttribWithThres (struct ata_smart_values *data, 
				struct ata_smart_thresholds *thresholds,
368
369
370
371
372
373
374
375
				int onlyfailed){
  int i,j;
  long long rawvalue;
  int needheader=1;
    
  // step through all vendor attributes
  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
    char *status;
ballen4705's avatar
ballen4705 committed
376
377
    struct ata_smart_attribute *disk=data->vendor_attributes+i;
    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
378
379
380
381
382
    
    // consider only valid attributes
    if (disk->id && thre->id){
      char *type;
      int failednow,failedever;
383
      char attributename[64];
ballen4705's avatar
ballen4705 committed
384

385
386
      failednow = (disk->current <= thre->threshold);
      failedever= (disk->worst   <= thre->threshold);
387
388
389
      
      // These break out of the loop if we are only printing certain entries...
      if (onlyfailed==1 && (!disk->status.flag.prefailure || !failednow))
390
	continue;
391
392
      
      if (onlyfailed==2 && !failedever)
393
	continue;
394
395
396
      
      // print header only if needed
      if (needheader){
397
	if (!onlyfailed){
ballen4705's avatar
ballen4705 committed
398
	  pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber);
399
	  pout("Vendor Specific SMART Attributes with Thresholds:\n");
400
	}
401
	pout("ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE     WHEN_FAILED RAW_VALUE\n");
402
403
404
405
406
	needheader=0;
      }
      
      // is this currently failed, or has it ever failed?
      if (failednow)
407
	status="FAILING_NOW";
408
      else if (failedever)
409
	status="In_the_past";
410
411
412
413
      else
	status="    -";
      
      // Print name of attribute
414
      ataPrintSmartAttribName(attributename,disk->id, con->attributedefs);
415
416
      pout("%-28s",attributename);

417
      // printing line for each valid attribute
418
      type=disk->status.flag.prefailure?"Pre-fail":"Old_age";
ballen4705's avatar
ballen4705 committed
419
420
421
      pout("0x%04x   %.3d   %.3d   %.3d    %-9s%-12s", 
	     (int)disk->status.all, (int)disk->current, (int)disk->worst,
	     (int)thre->threshold, type, status);
422
423
424
      
      // convert the six individual bytes to a long long (8 byte) integer
      rawvalue = 0;
425
426
427
428
429
430
431
432
433
434
435
      for (j=0; j<6; j++) {
	// This looks a bit roundabout, but is necessary.  Don't
	// succumb to the temptation to use raw[j]<<(8*j) since under
	// the normal rules this will be promoted to the native type.
	// On a 32 bit machine this might then overflow.
	long long temp;
	temp = disk->raw[j];
	temp <<= 8*j;
	rawvalue |= temp;
      }

436
437
438
439
440
      // This switch statement is where we handle Raw attributes
      // that are stored in an unusual vendor-specific format,
      switch (disk->id){
	// Power on time
      case 9:
441
	if (con->attributedefs[9]==1){
442
	  // minutes
ballen4705's avatar
ballen4705 committed
443
444
445
446
	  long long tmp1=rawvalue/60;
	  long long tmp2=rawvalue%60;
	  pout("%llu h + %2llu m\n", tmp1, tmp2);
	}
447
	else
ballen4705's avatar
ballen4705 committed
448
	  // hours
449
	  pout("%llu\n", rawvalue);  //stored in hours
450
451
452
453
	break;
	
	// Temperature
      case 194:
ballen4705's avatar
ballen4705 committed
454
	pout("%d", (int)disk->raw[0]);
455
	if (rawvalue==disk->raw[0])
456
	  pout("\n");
457
458
	else
	  // The other bytes are in use. Try IBM's model
ballen4705's avatar
ballen4705 committed
459
460
	  pout(" (Lifetime Min/Max %d/%d)\n",(int)disk->raw[2],
		 (int)disk->raw[4]);
461
462
	break;
      default:
463
	pout("%llu\n", rawvalue);
464
465
466
467
468
      }
      
      // print a warning if there is inconsistency here!
      if (disk->id != thre->id){
	char atdat[64],atthr[64];
469
470
	ataPrintSmartAttribName(atdat, disk->id, con->attributedefs);
	ataPrintSmartAttribName(atthr, thre->id, con->attributedefs);
471
472
473
	pout("%-28s<== Data Page      |  WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat);
	pout("%-28s<== Threshold Page |  INCONSISTENT IDENTITIES IN THE DATA\n",atthr);
      }
474
475
    }
  }
476
  if (!needheader) pout("\n");
ballen4705's avatar
ballen4705 committed
477
478
479
}


ballen4705's avatar
ballen4705 committed
480
void ataPrintGeneralSmartValues(struct ata_smart_values *data){
481
  pout("General SMART Values:\n");
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  
  PrintSmartOfflineStatus(data); 
  
  if (isSupportSelfTest(data)){
    PrintSmartSelfExecStatus (data);
  }
  
  PrintSmartTotalTimeCompleteOffline(data);
  PrintSmartOfflineCollectCap(data);
  PrintSmartCapability(data);
  
  PrintSmartErrorLogCapability(data);
  if (isSupportSelfTest(data)){
    PrintSmartShortSelfTestPollingTime (data);
    PrintSmartExtendedSelfTestPollingTime (data);
  }
  pout("\n");
ballen4705's avatar
ballen4705 committed
499
500
501
502
503
504
505
506
507
508
}

// Returns nonzero if region of memory contains non-zero entries
int nonempty(unsigned char *testarea,int n){
  int i;
  for (i=0;i<n;i++)
    if (testarea[i])
      return 1;
  return 0;
}
509
510

// returns number of errors
ballen4705's avatar
ballen4705 committed
511
void ataPrintSmartErrorlog (struct ata_smart_errorlog *data){
ballen4705's avatar
ballen4705 committed
512
513
  int i,j,k;
  
ballen4705's avatar
ballen4705 committed
514
  pout("SMART Error Log Version: %d\n", (int)data->revnumber);
ballen4705's avatar
ballen4705 committed
515
516
  
  // if no errors logged, return
ballen4705's avatar
ballen4705 committed
517
  if (!data->error_log_pointer){
518
    pout("No Errors Logged\n\n");
519
520
    return;
  }
521
  QUIETON(con);
522
523
  // If log pointer out of range, return
  if (data->error_log_pointer>5){
524
    pout("Invalid Error Log index = %02x (T13/1321D rev 1c"
525
	 "Section 8.41.6.8.2.2 gives valid range from 1 to 5)\n\n",
ballen4705's avatar
ballen4705 committed
526
	 (int)data->error_log_pointer);
ballen4705's avatar
ballen4705 committed
527
528
    return;
  }
529
530
531
532
533
534

  // Some internal consistency checking of the data structures
  if ((data->ata_error_count-data->error_log_pointer)%5) {
    pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n",
	 data->ata_error_count,data->error_log_pointer);
  }
ballen4705's avatar
ballen4705 committed
535
536
  
  // starting printing error log info
ballen4705's avatar
ballen4705 committed
537
538
  if (data->ata_error_count<=5)
    pout( "ATA Error Count: %d\n", (int)data->ata_error_count);
ballen4705's avatar
ballen4705 committed
539
  else
ballen4705's avatar
ballen4705 committed
540
    pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
ballen4705's avatar
ballen4705 committed
541
	   (int)data->ata_error_count);
542
  QUIETOFF(con);
543
544
545
546
547
548
549
550
551
552
553
  pout("\tDCR = Device Control Register\n");
  pout("\tFR  = Features Register\n");
  pout("\tSC  = Sector Count Register\n");
  pout("\tSN  = Sector Number Register\n");
  pout("\tCL  = Cylinder Low Register\n");
  pout("\tCH  = Cylinder High Register\n");
  pout("\tD/H = Device/Head Register\n");
  pout("\tCR  = Content written to Command Register\n");
  pout("\tER  = Error register\n");
  pout("\tSTA = Status register\n");
  pout("Timestamp is seconds since the previous disk power-on.\n");
ballen4705's avatar
ballen4705 committed
554
  pout("Note: timestamp \"wraps\" after 2^32 msec = 49.710 days.\n\n");
ballen4705's avatar
ballen4705 committed
555
556
557
558
559
  
  // now step through the five error log data structures (table 39 of spec)
  for (k = 4; k >= 0; k-- ) {
    
    // The error log data structure entries are a circular buffer
ballen4705's avatar
ballen4705 committed
560
    i=(data->error_log_pointer+k)%5;
ballen4705's avatar
ballen4705 committed
561
562
    
    // Spec says: unused error log structures shall be zero filled
ballen4705's avatar
ballen4705 committed
563
    if (nonempty((unsigned char*)&(data->errorlog_struct[i]),sizeof(data->errorlog_struct[i]))){
ballen4705's avatar
ballen4705 committed
564
      char *msgstate;
ballen4705's avatar
ballen4705 committed
565
      switch (data->errorlog_struct[i].error_struct.state){
ballen4705's avatar
ballen4705 committed
566
567
568
569
570
571
572
573
      case 0x00: msgstate="in an unknown state";break;
      case 0x01: msgstate="sleeping"; break;
      case 0x02: msgstate="in standby mode"; break;
      case 0x03: msgstate="active or idle"; break;
      case 0x04: msgstate="doing SMART off-line or self test"; break;
      default:   msgstate="in a vendor specific or reserved state";
      }
      // See table 42 of ATA5 spec
574
      QUIETON(con);
ballen4705's avatar
ballen4705 committed
575
      pout("Error %d occurred at disk power-on lifetime: %d hours\n",
576
	     (int)(data->ata_error_count+k-4), (int)data->errorlog_struct[i].error_struct.timestamp);
577
      QUIETOFF(con);
578
579
580
      pout("When the command that caused the error occurred, the device was %s.\n",msgstate);
      pout("After command completion occurred, registers were:\n");
      pout("ER:%02x SC:%02x SN:%02x CL:%02x CH:%02x D/H:%02x ST:%02x\n",
ballen4705's avatar
ballen4705 committed
581
582
583
584
585
586
587
	   (int)data->errorlog_struct[i].error_struct.error_register,
	   (int)data->errorlog_struct[i].error_struct.sector_count,
	   (int)data->errorlog_struct[i].error_struct.sector_number,
	   (int)data->errorlog_struct[i].error_struct.cylinder_low,
	   (int)data->errorlog_struct[i].error_struct.cylinder_high,
	   (int)data->errorlog_struct[i].error_struct.drive_head,
	   (int)data->errorlog_struct[i].error_struct.status);
588
589
      pout("Sequence of commands leading to the command that caused the error were:\n");
      pout("DCR   FR   SC   SN   CL   CH   D/H   CR   Timestamp\n");
ballen4705's avatar
ballen4705 committed
590
      for ( j = 4; j >= 0; j--){
ballen4705's avatar
ballen4705 committed
591
	struct ata_smart_errorlog_command_struct *thiscommand=&(data->errorlog_struct[i].commands[j]);
ballen4705's avatar
ballen4705 committed
592
593
594
	
	// Spec says: unused data command structures shall be zero filled
	if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand)))
ballen4705's avatar
ballen4705 committed
595
596
597
598
599
600
601
602
603
604
605
	  pout(" %02x   %02x   %02x   %02x   %02x   %02x    %02x   %02x     %d.%03d\n", 
	       (int)thiscommand->devicecontrolreg,
	       (int)thiscommand->featuresreg,
	       (int)thiscommand->sector_count,
	       (int)thiscommand->sector_number,
	       (int)thiscommand->cylinder_low,
	       (int)thiscommand->cylinder_high,
	       (int)thiscommand->drive_head,
	       (int)thiscommand->commandreg,
	       (unsigned int)(thiscommand->timestamp / 1000U),
	       (unsigned int)(thiscommand->timestamp % 1000U)); 
606
607
      }
      pout("\n");
ballen4705's avatar
ballen4705 committed
608
    }
609
  }
610
611
  QUIETON(con);
  if (con->quietmode)
612
    pout("\n");
613
  QUIETOFF(con);
ballen4705's avatar
ballen4705 committed
614
615
616
  return;  
}

617
// return value is number of entries found where the self-test showed an error
ballen4705's avatar
ballen4705 committed
618
int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *data,int allentries){
619
  int i,j,noheaderprinted=1;
620
  int retval=0;
ballen4705's avatar
ballen4705 committed
621

622
  if (allentries)
ballen4705's avatar
ballen4705 committed
623
624
    pout("SMART Self-test log, version number %d\n",(int)data->revnumber);
  if ((data->revnumber!=0x0001) && allentries)
625
    pout("Warning - structure revision number does not match spec!\n");
ballen4705's avatar
ballen4705 committed
626
  if (data->mostrecenttest==0){
627
    if (allentries)
628
629
      pout("No self-tests have been logged\n\n");
    return 0;
ballen4705's avatar
ballen4705 committed
630
631
632
  }

  // print log      
633
  for (i=20;i>=0;i--){    
ballen4705's avatar
ballen4705 committed
634
635
    struct ata_smart_selftestlog_struct *log;
    // log is a circular buffer
ballen4705's avatar
ballen4705 committed
636
637
    j=(i+data->mostrecenttest)%21;
    log=data->selftest_struct+j;
ballen4705's avatar
ballen4705 committed
638
639

    if (nonempty((unsigned char*)log,sizeof(*log))){
640
      char *msgtest,*msgstat,percent[64],firstlba[64];
641
      int errorfound=0;
ballen4705's avatar
ballen4705 committed
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658

      // test name
      switch(log->selftestnumber){
      case   0: msgtest="Off-line           "; break;
      case   1: msgtest="Short off-line     "; break;
      case   2: msgtest="Extended off-line  "; break;
      case 127: msgtest="Abort off-line test"; break;
      case 129: msgtest="Short captive      "; break;
      case 130: msgtest="Extended captive   "; break;
      default:  msgtest="Unknown test       ";
      }
      
      // test status
      switch((log->selfteststatus)>>4){
      case  0:msgstat="Completed                    "; break;
      case  1:msgstat="Aborted by host              "; break;
      case  2:msgstat="Interrupted (host reset)     "; break;
659
660
661
662
663
      case  3:msgstat="Fatal or unknown error       "; errorfound=1; break;
      case  4:msgstat="Completed: unknown failure   "; errorfound=1; break;
      case  5:msgstat="Completed: electrical failure"; errorfound=1; break;
      case  6:msgstat="Completed: servo/seek failure"; errorfound=1; break;
      case  7:msgstat="Completed: read failure      "; errorfound=1; break;
ballen4705's avatar
ballen4705 committed
664
665
666
      case 15:msgstat="Test in progress             "; break;
      default:msgstat="Unknown test status          ";
      }
667

668
      retval+=errorfound;
ballen4705's avatar
ballen4705 committed
669
      sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf);
670
671
672
673
674
675
676
677
678
679
680
681
682
683

      // T13/1321D revision 1c: (Data structure Rev #1)

      //The failing LBA shall be the LBA of the uncorrectable sector
      //that caused the test to fail. If the device encountered more
      //than one uncorrectable sector during the test, this field
      //shall indicate the LBA of the first uncorrectable sector
      //encountered. If the test passed or the test failed for some
      //reason other than an uncorrectable sector, the value of this
      //field is undefined.

      // This is true in ALL ATA-5 specs
      
      if (!errorfound || log->lbafirstfailure==0xffffffff || log->lbafirstfailure==0x00000000)
684
	sprintf(firstlba,"%s","-");
ballen4705's avatar
ballen4705 committed
685
686
      else	
	sprintf(firstlba,"0x%08x",log->lbafirstfailure);
687
688

      if (noheaderprinted && (allentries || errorfound)){
689
	pout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
690
691
692
693
	noheaderprinted=0;
      }
      
      if (allentries || errorfound)
ballen4705's avatar
ballen4705 committed
694
695
	pout("#%2d  %s %s %s  %8d         %s\n",21-i,msgtest,msgstat,
	     percent,(int)log->timestamp,firstlba);
ballen4705's avatar
ballen4705 committed
696
697
    }
  }
698
699
  if (!allentries && retval)
    pout("\n");
700
  return retval;
ballen4705's avatar
ballen4705 committed
701
702
}

ballen4705's avatar
ballen4705 committed
703
704
void ataPseudoCheckSmart ( struct ata_smart_values *data, 
                           struct ata_smart_thresholds *thresholds) {
705
706
707
  int i;
  int failed = 0;
  for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) {
ballen4705's avatar
ballen4705 committed
708
709
710
711
712
713
    if (data->vendor_attributes[i].id &&   
	thresholds->thres_entries[i].id &&
	data->vendor_attributes[i].status.flag.prefailure &&
	(data->vendor_attributes[i].current <= thresholds->thres_entries[i].threshold) &&
	(thresholds->thres_entries[i].threshold != 0xFE)){
      pout("Attribute ID %d Failed\n",(int)data->vendor_attributes[i].id);
714
715
716
      failed = 1;
    } 
  }   
717
  pout("%s\n", ( failed )?
718
719
720
	 "SMART overall-health self-assessment test result: FAILED!\n"
	 "Drive failure expected in less than 24 hours. SAVE ALL DATA":
	 "SMART overall-health self-assessment test result: PASSED");
ballen4705's avatar
ballen4705 committed
721
722
723
}


ballen4705's avatar
ballen4705 committed
724
725
// Compares failure type to policy in effect, and either exits or
// simply returns to the calling routine.
726
void failuretest(int type, int returnvalue){
ballen4705's avatar
ballen4705 committed
727
728
729
730

  // If this is an error in an "optional" SMART command
  if (type==OPTIONAL_CMD){
    if (con->conservative){
731
      pout("An optional SMART command has failed: exiting.  To continue, set the tolerance level to something other than 'conservative'\n");
ballen4705's avatar
ballen4705 committed
732
733
734
735
736
737
738
739
740
      exit(returnvalue);
    }
    return;
  }

  // If this is an error in a "mandatory" SMART command
  if (type==MANDATORY_CMD){
    if (con->permissive)
      return;
741
    pout("A mandatory SMART command has failed: exiting. To continue, use the -T option to set the tolerance level to 'permissive'\n");
ballen4705's avatar
ballen4705 committed
742
743
744
745
746
747
748
    exit(returnvalue);
  }

  fprintf(stderr,"Smartctl internal error in failuretest(type=%d). Please contact %s\n",type,PROJECTHOME);
  exit(returnvalue|FAILCMD);
}

749
750
// Used to warn users about invalid checksums.  Action to be taken may be
// altered by the user.
ballen4705's avatar
ballen4705 committed
751
void checksumwarning(const char *string){
752
753
754
755
  // user has asked us to ignore checksum errors
  if (con->checksumignore)
        return;

ballen4705's avatar
ballen4705 committed
756
  pout("Warning! %s error: invalid SMART checksum.\n",string);
757
758

  // user has asked us to fail on checksum errors
ballen4705's avatar
ballen4705 committed
759
760
  if (con->checksumfail)
    exit(FAILSMART);
761

ballen4705's avatar
ballen4705 committed
762
763
  return;
}
764
765
766
767
768
769
770
771

// Initialize to zero just in case some SMART routines don't work
struct hd_driveid drive;
struct ata_smart_values smartval;
struct ata_smart_thresholds smartthres;
struct ata_smart_errorlog smarterror;
struct ata_smart_selftestlog smartselftest;

772
int ataPrintMain (int fd){
773
  int timewait,code;
774
  int returnval=0;
775

776
777
  // Start by getting Drive ID information.  We need this, to know if SMART is supported.
  if (ataReadHDIdentity(fd,&drive)){
778
    pout("Smartctl: Hard Drive Read Identity Failed\n\n");
ballen4705's avatar
ballen4705 committed
779
    failuretest(MANDATORY_CMD, returnval|=FAILID);
780
  }
781
782
  
  // Print most drive identity information if requested
783
  if (con->driveinfo){
784
    pout("=== START OF INFORMATION SECTION ===\n");
ballen4705's avatar
ballen4705 committed
785
    ataPrintDriveInfo(&drive);
786
787
788
  }
  
  // now check if drive supports SMART; otherwise time to exit
ballen4705's avatar
ballen4705 committed
789
  if (!ataSmartSupport(&drive)){
790
    pout("SMART support is: Unavailable - device lacks SMART capability.\n");
ballen4705's avatar
ballen4705 committed
791
    failuretest(MANDATORY_CMD, returnval|=FAILSMART);
792
    pout("                  Checking to be sure by trying SMART ENABLE command.\n");
793
    if (ataEnableSmart(fd)){
794
      pout("                  No SMART functionality found. Sorry.\n");
ballen4705's avatar
ballen4705 committed
795
      failuretest(MANDATORY_CMD,returnval|=FAILSMART);
796
797
    }
    else
798
      pout("                  SMART appears to work.  Continuing.\n"); 
799
    if (!con->driveinfo) pout("\n");
800
801
802
  }
  
  // Now print remaining drive info: is SMART enabled?    
803
  if (con->driveinfo){
804
    pout("SMART support is: Available - device has SMART capability.\n");
805
    if (ataDoesSmartWork(fd))
806
      pout("SMART support is: Enabled\n");
807
    else
808
809
      pout("SMART support is: Disabled\n");
    pout("\n");
810
  }
811

812
813
  
  // START OF THE ENABLE/DISABLE SECTION OF THE CODE
814
815
816
  if (con->smartenable || con->smartdisable || 
      con->smartautosaveenable || con->smartautosavedisable || 
      con->smartautoofflineenable || con->smartautoofflinedisable)
817
    pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
818
  
819
  // Enable/Disable SMART commands
820
  if (con->smartenable){
821
    if (ataEnableSmart(fd)) {
822
      pout("Smartctl: SMART Enable Failed.\n\n");
ballen4705's avatar
ballen4705 committed
823
      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
ballen4705's avatar
ballen4705 committed
824
    }
825
    else
826
      pout("SMART Enabled.\n");
827
828
829
830
  }
  
  // From here on, every command requires that SMART be enabled...
  if (!ataDoesSmartWork(fd)) {
831
    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
832
    return returnval;
833
834
835
  }
  
  // Turn off SMART on device
836
  if (con->smartdisable){    
837
    if (ataDisableSmart(fd)) {
838
      pout( "Smartctl: SMART Disable Failed.\n\n");
ballen4705's avatar
ballen4705 committed
839
      failuretest(MANDATORY_CMD,returnval|=FAILSMART);
ballen4705's avatar
ballen4705 committed
840
    }
841
    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
842
    return returnval;		
843
844
  }
  
845
846
  // Let's ALWAYS issue this command to get the SMART status
  code=ataSmartStatus2(fd);
847
  if (code==-1)
ballen4705's avatar
ballen4705 committed
848
849
    failuretest(MANDATORY_CMD, returnval|=FAILSMART);

850
  // Enable/Disable Auto-save attributes
851
  if (con->smartautosaveenable){
852
    if (ataEnableAutoSave(fd)){
853
      pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
ballen4705's avatar
ballen4705 committed
854
      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
855
    }
856
    else
857
      pout("SMART Attribute Autosave Enabled.\n");
858
  }
859
  if (con->smartautosavedisable){
860
    if (ataDisableAutoSave(fd)){
861
      pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
ballen4705's avatar
ballen4705 committed
862
      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
863
    }
864
    else
865
      pout("SMART Attribute Autosave Disabled.\n");
866
867
868
  }
  
  // for everything else read values and thresholds are needed
869
  if (ataReadSmartValues(fd, &smartval)){
870
    pout("Smartctl: SMART Read Values failed.\n\n");
ballen4705's avatar
ballen4705 committed
871
    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
872
873
  }
  if (ataReadSmartThresholds(fd, &smartthres)){
874
    pout("Smartctl: SMART Read Thresholds failed.\n\n");
ballen4705's avatar
ballen4705 committed
875
    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
876
877
  }

878
  // Enable/Disable Off-line testing
879
  if (con->smartautoofflineenable){
ballen4705's avatar
ballen4705 committed
880
    if (!isSupportAutomaticTimer(&smartval)){
881
      pout("Warning: device does not support SMART Automatic Timers.\n\n");
ballen4705's avatar
ballen4705 committed
882
      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
ballen4705's avatar
ballen4705 committed
883
    }
884
    if (ataEnableAutoOffline(fd)){
885
      pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n");
ballen4705's avatar
ballen4705 committed
886