ataprint.cpp 34 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>
28
#include "atacmds.h"
ballen4705's avatar
ballen4705 committed
29
30
31
32
#include "ataprint.h"
#include "smartctl.h"
#include "extern.h"

33
const char *CVSid2="$Id: ataprint.cpp,v 1.47 2002/11/29 10:41:58 ballen4705 Exp $"
34
35
36
37
CVSID1 CVSID2 CVSID3 CVSID6;

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

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 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)
59
    pout("%.*s\n",(int)(n-i),out+i);
60
  else
61
    pout("[No Information Found]\n");
62
63
64
65
66

  return;
}


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

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

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

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

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

  // unrecognized minor revision code
  if (!description){
ballen4705's avatar
ballen4705 committed
88
    sprintf(unknown,"Unrecognized. Minor revision code: 0x%02hx",minorrev);
89
90
91
    description=unknown;
  }
  
92
93
94
95
96
97
98
  
  // 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
99
  pout("ATA Version is:   %d\n",(int)abs(version));
100
  pout("ATA Standard is:  %s\n",description);
101
  
102
103
  if (version>=3)
    return;
104
  
105
106
  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");
107
  return;
ballen4705's avatar
ballen4705 committed
108
109
110
}


ballen4705's avatar
ballen4705 committed
111
112
/*  prints verbose value Off-line data collection status byte */
void PrintSmartOfflineStatus(struct ata_smart_values *data){
113
114
  pout("Off-line data collection status: ");	
  
ballen4705's avatar
ballen4705 committed
115
  switch(data->offline_data_collection_status){
116
117
118
  case 0x00:
  case 0x80:
    pout("(0x%02x)\tOffline data collection activity was\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
119
	 (int)data->offline_data_collection_status);
120
121
122
123
124
    pout("never started.\n");
    break;
  case 0x01:
  case 0x81:
    pout("(0x%02x)\tReserved.\n",
ballen4705's avatar
ballen4705 committed
125
	 (int)data->offline_data_collection_status);
126
127
128
129
    break;
  case 0x02:
  case 0x82:
    pout("(0x%02x)\tOffline data collection activity \n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
130
	 (int)data->offline_data_collection_status);
131
132
133
134
135
    pout("completed without error.\n");
    break;
  case 0x03:
  case 0x83:
    pout("(0x%02x)\tReserved.\n",
ballen4705's avatar
ballen4705 committed
136
	 (int)data->offline_data_collection_status);
137
138
139
140
    break;
  case 0x04:
  case 0x84:
    pout("(0x%02x)\tOffline data collection activity was \n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
141
	 (int)data->offline_data_collection_status);
142
143
144
145
146
    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
147
	 (int)data->offline_data_collection_status);
148
149
150
151
152
    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
153
	 (int)data->offline_data_collection_status);
154
155
156
    pout("aborted by the device with a fatal error.\n");
    break;
  default:
ballen4705's avatar
ballen4705 committed
157
158
159
160
161
    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);
162
    else
ballen4705's avatar
ballen4705 committed
163
      pout("(0x%02x)\tReserved.\n",(int)data->offline_data_collection_status);
164
  }
ballen4705's avatar
ballen4705 committed
165
166
167
168
}



ballen4705's avatar
ballen4705 committed
169
void PrintSmartSelfExecStatus(struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
170
{
171
   pout("Self-test execution status:      ");
ballen4705's avatar
ballen4705 committed
172
   
ballen4705's avatar
ballen4705 committed
173
   switch (data->self_test_exec_status >> 4)
ballen4705's avatar
ballen4705 committed
174
175
   {
      case 0:
176
        pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
177
                (int)data->self_test_exec_status);
178
        pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
ballen4705's avatar
ballen4705 committed
179
180
        break;
       case 1:
181
         pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
182
                 (int)data->self_test_exec_status);
183
         pout("the host.\n");
ballen4705's avatar
ballen4705 committed
184
185
         break;
       case 2:
186
         pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
187
                 (int)data->self_test_exec_status);
188
         pout("by the host with a hard or soft reset.\n");
ballen4705's avatar
ballen4705 committed
189
190
         break;
       case 3:
191
          pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
192
                  (int)data->self_test_exec_status);
193
194
195
196
          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
197
198
          break;
       case 4:
199
          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
200
                  (int)data->self_test_exec_status);
201
202
          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
203
204
          break;
       case 5:
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("the electrical element of the test\n\t\t\t\t\t");
          pout("failed.\n");
ballen4705's avatar
ballen4705 committed
209
210
          break;
       case 6:
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 servo (and/or seek) element of the \n\t\t\t\t\t");
          pout("test failed.\n");
ballen4705's avatar
ballen4705 committed
215
216
          break;
       case 7:
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
          pout("the read element of the test failed.\n");
ballen4705's avatar
ballen4705 committed
220
221
          break;
       case 15:
222
          pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t",
ballen4705's avatar
ballen4705 committed
223
                  (int)data->self_test_exec_status);
224
          pout("%1d0%% of test remaining.\n", 
ballen4705's avatar
ballen4705 committed
225
                  (int)(data->self_test_exec_status & 0x0f));
ballen4705's avatar
ballen4705 committed
226
227
          break;
       default:
228
          pout("(%4d)\tReserved.\n",
ballen4705's avatar
ballen4705 committed
229
                  (int)data->self_test_exec_status);
ballen4705's avatar
ballen4705 committed
230
231
232
233
234
235
236
          break;
   }
	
}



ballen4705's avatar
ballen4705 committed
237
void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){
238
239
  pout("Total time to complete off-line \n");
  pout("data collection: \t\t (%4d) seconds.\n", 
ballen4705's avatar
ballen4705 committed
240
       (int)data->total_time_to_complete_off_line);
ballen4705's avatar
ballen4705 committed
241
242
243
244
}



ballen4705's avatar
ballen4705 committed
245
void PrintSmartOfflineCollectCap(struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
246
{
247
248
   pout("Offline data collection\n");
   pout("capabilities: \t\t\t (0x%02x) ",
ballen4705's avatar
ballen4705 committed
249
            (int)data->offline_data_collection_capability);
ballen4705's avatar
ballen4705 committed
250

ballen4705's avatar
ballen4705 committed
251
   if (data->offline_data_collection_capability == 0x00)
ballen4705's avatar
ballen4705 committed
252
   {
253
      pout("\tOff-line data collection not supported.\n");
ballen4705's avatar
ballen4705 committed
254
255
256
   } 
   else 
   {
257
      pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
258
259
              "SMART execute Offline immediate." :
              "No SMART execute Offline immediate.");
ballen4705's avatar
ballen4705 committed
260

261
      pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? 
262
263
              "Automatic timer ON/OFF support.":
              "No Automatic timer ON/OFF support.");
ballen4705's avatar
ballen4705 committed
264
		
265
      pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? 
266
267
              "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
268

269
      pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? 
270
271
              "Offline surface scan supported.":
              "No Offline surface scan supported.");
ballen4705's avatar
ballen4705 committed
272

273
      pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? 
274
275
              "Self-test supported.":
              "No Self-test supported.");
ballen4705's avatar
ballen4705 committed
276
277
278
279
280
    }
}



ballen4705's avatar
ballen4705 committed
281
void PrintSmartCapability ( struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
282
{
283
   pout("SMART capabilities:            ");
ballen4705's avatar
ballen4705 committed
284
   pout("(0x%04x)\t", (int)data->smart_capability);
ballen4705's avatar
ballen4705 committed
285
   
ballen4705's avatar
ballen4705 committed
286
   if (data->smart_capability == 0x00)
ballen4705's avatar
ballen4705 committed
287
   {
288
       pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
ballen4705's avatar
ballen4705 committed
289
290
291
292
   } 
   else 
   {
	
ballen4705's avatar
ballen4705 committed
293
      pout( "%s\n", (data->smart_capability & 0x01)? 
294
295
              "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
296
		
ballen4705's avatar
ballen4705 committed
297
      if ( data->smart_capability & 0x02 )
ballen4705's avatar
ballen4705 committed
298
      {
299
          pout("\t\t\t\t\tSupports SMART auto save timer.\n");
ballen4705's avatar
ballen4705 committed
300
301
302
303
304
305
      }
   }
}



ballen4705's avatar
ballen4705 committed
306
void PrintSmartErrorLogCapability ( struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
307
308
{

309
   pout("Error logging capability:       ");
ballen4705's avatar
ballen4705 committed
310
311
312
    
   if ( isSmartErrorLogCapable(data) )
   {
313
      pout(" (0x%02x)\tError logging supported.\n",
ballen4705's avatar
ballen4705 committed
314
               (int)data->errorlog_capability);
ballen4705's avatar
ballen4705 committed
315
316
   }
   else {
317
       pout(" (0x%02x)\tError logging NOT supported.\n",
ballen4705's avatar
ballen4705 committed
318
                (int)data->errorlog_capability);
ballen4705's avatar
ballen4705 committed
319
320
321
322
323
   }
}



ballen4705's avatar
ballen4705 committed
324
void PrintSmartShortSelfTestPollingTime (struct ata_smart_values *data)
ballen4705's avatar
ballen4705 committed
325
326
327
{
   if ( isSupportSelfTest(data) )
   {
328
329
      pout("Short self-test routine \n");
      pout("recommended polling time: \t (%4d) minutes.\n", 
ballen4705's avatar
ballen4705 committed
330
               (int)data->short_test_completion_time);
ballen4705's avatar
ballen4705 committed
331
332
333
334

   }
   else
   {
335
336
      pout("Short self-test routine \n");
      pout("recommended polling time: \t        Not Supported.\n");
ballen4705's avatar
ballen4705 committed
337
338
339
340
   }
}


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


357
358
359
// 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
360
361
void PrintSmartAttribWithThres (struct ata_smart_values *data, 
				struct ata_smart_thresholds *thresholds,
362
363
364
365
366
367
368
369
				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
370
371
    struct ata_smart_attribute *disk=data->vendor_attributes+i;
    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
372
373
374
375
376
    
    // consider only valid attributes
    if (disk->id && thre->id){
      char *type;
      int failednow,failedever;
377
      char attributename[64];
ballen4705's avatar
ballen4705 committed
378

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

411
      // printing line for each valid attribute
412
      type=disk->status.flag.prefailure?"Pre-fail":"Old_age";
ballen4705's avatar
ballen4705 committed
413
414
415
      pout("0x%04x   %.3d   %.3d   %.3d    %-9s%-12s", 
	     (int)disk->status.all, (int)disk->current, (int)disk->worst,
	     (int)thre->threshold, type, status);
416
417
418
      
      // convert the six individual bytes to a long long (8 byte) integer
      rawvalue = 0;
419
420
421
422
423
424
425
426
427
428
429
      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;
      }

430
431
432
433
434
      // 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:
ballen4705's avatar
ballen4705 committed
435
	if (con->smart009minutes){
436
	  // minutes
ballen4705's avatar
ballen4705 committed
437
438
439
440
	  long long tmp1=rawvalue/60;
	  long long tmp2=rawvalue%60;
	  pout("%llu h + %2llu m\n", tmp1, tmp2);
	}
441
	else
ballen4705's avatar
ballen4705 committed
442
	  // hours
443
	  pout("%llu\n", rawvalue);  //stored in hours
444
445
446
447
	break;
	
	// Temperature
      case 194:
ballen4705's avatar
ballen4705 committed
448
	pout("%d", (int)disk->raw[0]);
449
	if (rawvalue==disk->raw[0])
450
	  pout("\n");
451
452
	else
	  // The other bytes are in use. Try IBM's model
ballen4705's avatar
ballen4705 committed
453
454
	  pout(" (Lifetime Min/Max %d/%d)\n",(int)disk->raw[2],
		 (int)disk->raw[4]);
455
456
	break;
      default:
457
	pout("%llu\n", rawvalue);
458
459
460
461
462
463
464
465
466
467
      }
      
      // print a warning if there is inconsistency here!
      if (disk->id != thre->id){
	char atdat[64],atthr[64];
	ataPrintSmartAttribName(atdat,disk->id);
	ataPrintSmartAttribName(atthr,thre->id);
	pout("%-28s<== Data Page      |  WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat);
	pout("%-28s<== Threshold Page |  INCONSISTENT IDENTITIES IN THE DATA\n",atthr);
      }
468
469
    }
  }
470
  if (!needheader) pout("\n");
ballen4705's avatar
ballen4705 committed
471
472
473
}


ballen4705's avatar
ballen4705 committed
474
void ataPrintGeneralSmartValues(struct ata_smart_values *data){
475
  pout("General SMART Values:\n");
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  
  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
493
494
495
496
497
498
499
500
501
502
}

// 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;
}
503
504

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

  // 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
529
530
  
  // starting printing error log info
ballen4705's avatar
ballen4705 committed
531
532
  if (data->ata_error_count<=5)
    pout( "ATA Error Count: %d\n", (int)data->ata_error_count);
ballen4705's avatar
ballen4705 committed
533
  else
ballen4705's avatar
ballen4705 committed
534
    pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
ballen4705's avatar
ballen4705 committed
535
	   (int)data->ata_error_count);
536
  QUIETOFF(con);
537
538
539
540
541
542
543
544
545
546
547
  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
548
  pout("Note: timestamp \"wraps\" after 2^32 msec = 49.710 days.\n\n");
ballen4705's avatar
ballen4705 committed
549
550
551
552
553
  
  // 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
554
    i=(data->error_log_pointer+k)%5;
ballen4705's avatar
ballen4705 committed
555
556
    
    // Spec says: unused error log structures shall be zero filled
ballen4705's avatar
ballen4705 committed
557
    if (nonempty((unsigned char*)&(data->errorlog_struct[i]),sizeof(data->errorlog_struct[i]))){
ballen4705's avatar
ballen4705 committed
558
      char *msgstate;
ballen4705's avatar
ballen4705 committed
559
      switch (data->errorlog_struct[i].error_struct.state){
ballen4705's avatar
ballen4705 committed
560
561
562
563
564
565
566
567
      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
568
      QUIETON(con);
ballen4705's avatar
ballen4705 committed
569
      pout("Error %d occurred at disk power-on lifetime: %d hours\n",
570
	     (int)(data->ata_error_count+k-4), (int)data->errorlog_struct[i].error_struct.timestamp);
571
      QUIETOFF(con);
572
573
574
      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
575
576
577
578
579
580
581
	   (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);
582
583
      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
584
      for ( j = 4; j >= 0; j--){
ballen4705's avatar
ballen4705 committed
585
	struct ata_smart_errorlog_command_struct *thiscommand=&(data->errorlog_struct[i].commands[j]);
ballen4705's avatar
ballen4705 committed
586
587
588
	
	// Spec says: unused data command structures shall be zero filled
	if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand)))
ballen4705's avatar
ballen4705 committed
589
590
591
592
593
594
595
596
597
598
599
	  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)); 
600
601
      }
      pout("\n");
ballen4705's avatar
ballen4705 committed
602
    }
603
  }
604
605
  QUIETON(con);
  if (con->quietmode)
606
    pout("\n");
607
  QUIETOFF(con);
ballen4705's avatar
ballen4705 committed
608
609
610
  return;  
}

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

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

  // print log      
627
  for (i=20;i>=0;i--){    
ballen4705's avatar
ballen4705 committed
628
629
    struct ata_smart_selftestlog_struct *log;
    // log is a circular buffer
ballen4705's avatar
ballen4705 committed
630
631
    j=(i+data->mostrecenttest)%21;
    log=data->selftest_struct+j;
ballen4705's avatar
ballen4705 committed
632
633

    if (nonempty((unsigned char*)log,sizeof(*log))){
634
      char *msgtest,*msgstat,percent[64],firstlba[64];
635
      int errorfound=0;
ballen4705's avatar
ballen4705 committed
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652

      // 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;
653
654
655
656
657
      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
658
659
660
      case 15:msgstat="Test in progress             "; break;
      default:msgstat="Unknown test status          ";
      }
661

662
      retval+=errorfound;
ballen4705's avatar
ballen4705 committed
663
      sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf);
664
665
666
667
668
669
670
671
672
673
674
675
676
677

      // 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)
ballen4705's avatar
ballen4705 committed
678
679
680
	sprintf(firstlba,"%s","");
      else	
	sprintf(firstlba,"0x%08x",log->lbafirstfailure);
681
682

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

ballen4705's avatar
ballen4705 committed
697
698
void ataPseudoCheckSmart ( struct ata_smart_values *data, 
                           struct ata_smart_thresholds *thresholds) {
699
700
701
  int i;
  int failed = 0;
  for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) {
ballen4705's avatar
ballen4705 committed
702
703
704
705
706
707
    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);
708
709
710
      failed = 1;
    } 
  }   
711
  pout("%s\n", ( failed )?
712
713
714
	 "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
715
716
717
}


ballen4705's avatar
ballen4705 committed
718
719
// Compares failure type to policy in effect, and either exits or
// simply returns to the calling routine.
720
void failuretest(int type, int returnvalue){
ballen4705's avatar
ballen4705 committed
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752

  // If this is an error in an "optional" SMART command
  if (type==OPTIONAL_CMD){
    if (con->conservative){
      pout("An optional SMART command has failed: exiting.  To continue, turn off the -%c option\n",
	   ULTRACONSERVATIVE);
      exit(returnvalue);
    }
    return;
  }

  // If this is an error in a "mandatory" SMART command
  if (type==MANDATORY_CMD){
    if (con->permissive)
      return;
    pout("A mandatory SMART command has failed: exiting. To continue, turn on the -%c option\n",
	 PERMISSIVE);
    exit(returnvalue);
  }

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

// Used to warn users about invalid checksums.  However we will not
// abort on invalid checksums.
void checksumwarning(const char *string){
  pout("Warning! %s error: invalid SMART checksum.\n",string);
  if (con->checksumfail)
    exit(FAILSMART);
  return;
}
753
754
755
756
757
758
759
760

// 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;

761
int ataPrintMain (int fd){
762
  int timewait,code;
763
  int returnval=0;
764

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

801
802
  
  // START OF THE ENABLE/DISABLE SECTION OF THE CODE
803
804
805
  if (con->smartenable || con->smartdisable || 
      con->smartautosaveenable || con->smartautosavedisable || 
      con->smartautoofflineenable || con->smartautoofflinedisable)
806
    pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
807
  
808
  // Enable/Disable SMART commands
809
  if (con->smartenable){
810
    if (ataEnableSmart(fd)) {
811
      pout("Smartctl: SMART Enable Failed.\n\n");
ballen4705's avatar
ballen4705 committed
812
      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
ballen4705's avatar
ballen4705 committed
813
    }
814
    else
815
      pout("SMART Enabled.\n");
816
817
818
819
  }
  
  // From here on, every command requires that SMART be enabled...
  if (!ataDoesSmartWork(fd)) {
ballen4705's avatar
ballen4705 committed
820
    pout("SMART Disabled. Use option -%c to enable it.\n", (int)SMARTENABLE );
821
    return returnval;
822
823
824
  }
  
  // Turn off SMART on device
825
  if (con->smartdisable){    
826
    if (ataDisableSmart(fd)) {
827
      pout( "Smartctl: SMART Disable Failed.\n\n");
ballen4705's avatar
ballen4705 committed
828
      failuretest(MANDATORY_CMD,returnval|=FAILSMART);
ballen4705's avatar
ballen4705 committed
829
    }
ballen4705's avatar
ballen4705 committed
830
    pout("SMART Disabled. Use option -%c to enable it.\n",(int)SMARTENABLE);
831
    return returnval;		
832
833
  }
  
834
835
  // Let's ALWAYS issue this command to get the SMART status
  code=ataSmartStatus2(fd);
836
  if (code==-1)
ballen4705's avatar
ballen4705 committed
837
838
    failuretest(MANDATORY_CMD, returnval|=FAILSMART);

839
  // Enable/Disable Auto-save attributes
840
  if (con->smartautosaveenable){
841
    if (ataEnableAutoSave(fd)){
842
      pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
ballen4705's avatar
ballen4705 committed
843
      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
844
    }
845
    else
846
      pout("SMART Attribute Autosave Enabled.\n");
847
  }
848
  if (con->smartautosavedisable){
849
    if (ataDisableAutoSave(fd)){
850
      pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
ballen4705's avatar
ballen4705 committed
851
      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
852
    }
853
    else
854
      pout("SMART Attribute Autosave Disabled.\n");
855
856
857
  }
  
  // for everything else read values and thresholds are needed
858
  if (ataReadSmartValues(fd, &smartval)){
859
    pout("Smartctl: SMART Read Values failed.\n\n");
ballen4705's avatar
ballen4705 committed
860
    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
861
862
  }
  if (ataReadSmartThresholds(fd, &smartthres)){
863
    pout("Smartctl: SMART Read Thresholds failed.\n\n");
ballen4705's avatar
ballen4705 committed
864
    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
865
866
  }

867
  // Enable/Disable Off-line testing
868
  if (con->smartautoofflineenable){
ballen4705's avatar
ballen4705 committed
869
    if (!isSupportAutomaticTimer(&smartval)){
870
      pout("Warning: device does not support SMART Automatic Timers.\n\n");
ballen4705's avatar
ballen4705 committed
871
      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
ballen4705's avatar
ballen4705 committed
872
    }
873
    if (ataEnableAutoOffline(fd)){
874
      pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n");
ballen4705's avatar
ballen4705 committed
875
      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
876
    }
877
    else
878
      pout("SMART Automatic Offline Testing Enabled every four hours.\n");
879
  }
880
  if (con->smartautoofflinedisable){
ballen4705's avatar
ballen4705 committed
881
    if (!isSupportAutomaticTimer(&smartval)){
882
      pout("Warning: device does not support SMART Automatic Timers.\n\n");
ballen4705's avatar
ballen4705 committed
883
      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
884
885
    }
    if (ataDisableAutoOffline(fd)){
886
      pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n");
ballen4705's avatar
ballen4705 committed
887
      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
888
    }
889
    else
890
      pout("SMART Automatic Offline Testing Disabled.\n");
891
  }
ballen4705's avatar
ballen4705 committed
892

893
  // all this for a newline!
894
895
896
  if (con->smartenable || con->smartdisable || 
      con->smartautosaveenable || con->smartautosavedisable || 
      con->smartautoofflineenable || con->smartautoofflinedisable)