smartctl.cpp 11.8 KB
Newer Older
ballen4705's avatar
ballen4705 committed
1
2
3
/*
 * smartctl.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) 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
27
28
29
30
31
32
 */


#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <string.h>
33
#include <stdarg.h>
ballen4705's avatar
ballen4705 committed
34
35
36
37
38
#include "smartctl.h"
#include "atacmds.h"
#include "ataprint.h"
#include "scsicmds.h"
#include "scsiprint.h"
39
#include "extern.h"
ballen4705's avatar
ballen4705 committed
40

41
extern const char *CVSid1, *CVSid2, *CVSid3, *CVSid4; 
42
const char* CVSid5="$Id: smartctl.cpp,v 1.29 2002/11/25 08:40:48 ballen4705 Exp $"
43
CVSID1 CVSID2 CVSID3 CVSID4 CVSID5 CVSID6;
ballen4705's avatar
ballen4705 committed
44

45
46
47
// This is a block containing all the "control variables".  We declare
// this globally in this file, and externally in other files.
atamainctrl *con=NULL;
ballen4705's avatar
ballen4705 committed
48

49
void printslogan(){
50
  pout("smartctl version %d.%d-%d Copyright (C) 2002 Bruce Allen\n",
ballen4705's avatar
ballen4705 committed
51
      (int)RELEASE_MAJOR, (int)RELEASE_MINOR, (int)SMARTMONTOOLS_VERSION);
ballen4705's avatar
ballen4705 committed
52
  pout("Home page is %s\n\n",PROJECTHOME);
53
54
55
56
57
  return;
}


void printcopy(){
58
  char out[CVSMAXLEN];
59
60
61
62
63
  pout("smartctl comes with ABSOLUTELY NO WARRANTY. This\n");
  pout("is free software, and you are welcome to redistribute it\n");
  pout("under the terms of the GNU General Public License Version 2.\n");
  pout("See http://www.gnu.org for further details.\n\n");
  pout("CVS version IDs of files used to build this code are:\n");
64
65
66
67
  printone(out,CVSid1);
  pout("%s",out);
  printone(out,CVSid2);
  pout("%s",out);
68
69
  printone(out,CVSid3);
  pout("%s",out);
70
71
72
73
  printone(out,CVSid4);
  pout("%s",out);
  printone(out,CVSid5);
  pout("%s",out);
74
75
76
77
  return;
}

/*  void prints help information for command syntax */
ballen4705's avatar
ballen4705 committed
78
void Usage ( void){
ballen4705's avatar
ballen4705 committed
79
  printf("Usage: smartctl -[options] [device]\n");
ballen4705's avatar
ballen4705 committed
80
81
  printf("\nShow Information Options:\n");
  printf("  %c  Show Version, Copyright and License info\n", PRINTCOPYLEFT);
82
  printf("  %c  Show SMART Drive Info                   (ATA/SCSI)\n",DRIVEINFO);
ballen4705's avatar
ballen4705 committed
83
84
85
86
87
88
89
90
91
92
93
94
  printf("  %c  Show all SMART Information              (ATA/SCSI)\n",SMARTVERBOSEALL);
  printf("\nRun-time Behavior Options:\n");
  printf("  %c  Quiet: only show SMART Drive Errors     (ATA Only)\n",    QUIETMODE);
  printf("  %c  Very Quiet: no display, use Exit Status (ATA Only)\n",    VERYQUIETMODE);  
  printf("  %c  Device is an ATA Device                 (ATA Only)\n",    NOTSCSIDEVICE);
  printf("  %c  Device is a SCSI Device                 (SCSI Only)\n",   NOTATADEVICE);
  printf("  %c  Permissive: continue on Mandatory fails (ATA Only)\n",    PERMISSIVE);
  printf("  %c  Conservative: exit if Optional Cmd fail (ATA Only)\n",    ULTRACONSERVATIVE);  
  printf("  %c  Warning: exit if Struct Checksum bad    (ATA Only)\n",    EXITCHECKSUMERROR);
  printf("\nSMART Feature Enable/Disable Commands:\n");
  printf("  %c  Enable  SMART data collection           (ATA/SCSI)\n",    SMARTENABLE);
  printf("  %c  Disable SMART data collection           (ATA/SCSI)\n",    SMARTDISABLE);
95
96
97
98
  printf("  %c  Enable  SMART Automatic Offline Test    (ATA Only)\n",    SMARTAUTOOFFLINEENABLE);
  printf("  %c  Disable SMART Automatic Offline Test    (ATA Only)\n",    SMARTAUTOOFFLINEDISABLE);
  printf("  %c  Enable  SMART Attribute Autosave        (ATA Only)\n",    SMARTAUTOSAVEENABLE);
  printf("  %c  Disable SMART Attribute Autosave        (ATA Only)\n",    SMARTAUTOSAVEDISABLE);
ballen4705's avatar
ballen4705 committed
99
100
101
102
103
104
105
106
107
  printf("\nRead and Display Data Options:\n");
  printf("  %c  Show SMART Status                       (ATA/SCSI)\n",    CHECKSMART);
  printf("  %c  Show SMART General Attributes           (ATA Only)\n",    GENERALSMARTVALUES);
  printf("  %c  Show SMART Vendor Attributes            (ATA Only)\n",    SMARTVENDORATTRIB);
  printf("  %c  Show SMART Drive Error Log              (ATA Only\n",     SMARTERRORLOG);
  printf("  %c  Show SMART Drive Self Test Log          (ATA Only)\n",    SMARTSELFTESTLOG);
  printf("\nVendor-specific Attribute Display Options:\n");
  printf("  %c  Raw Attribute id=009 stored in minutes  (ATA Only)\n",    SMART009MINUTES);
  printf("\nSelf-Test Options (no more than one):\n");
108
109
110
111
112
113
  printf("  %c  Execute Off-line data collection        (ATA/SCSI)\n",    SMARTEXEOFFIMMEDIATE);
  printf("  %c  Execute Short Self Test                 (ATA/SCSI)\n",    SMARTSHORTSELFTEST );
  printf("  %c  Execute Short Self Test (Captive Mode)  (ATA/SCSI)\n",    SMARTSHORTCAPSELFTEST );
  printf("  %c  Execute Extended Self Test              (ATA/SCSI)\n",    SMARTEXTENDSELFTEST );
  printf("  %c  Execute Extended Self Test (Captive)    (ATA/SCSI)\n",    SMARTEXTENDCAPSELFTEST );
  printf("  %c  Execute Self Test Abort                 (ATA/SCSI)\n",    SMARTSELFTESTABORT );
ballen4705's avatar
ballen4705 committed
114
  printf("\nExamples:\n");
115
116
117
118
  printf("  smartctl -etf /dev/hda  (Enables SMART on first disk)\n");
  printf("  smartctl -a   /dev/hda  (Prints all SMART information)\n");
  printf("  smartctl -X   /dev/hda  (Executes extended disk self-test)\n");
  printf("  smartctl -qvL /dev/hda  (Prints Self-Test & Attribute errors.)\n");
ballen4705's avatar
ballen4705 committed
119
120
}

121
122
123
124
125
126
const char opts[] = { 
  DRIVEINFO, CHECKSMART, SMARTVERBOSEALL, SMARTVENDORATTRIB,
  GENERALSMARTVALUES, SMARTERRORLOG, SMARTSELFTESTLOG, SMARTDISABLE,
  SMARTENABLE, SMARTAUTOOFFLINEENABLE, SMARTAUTOOFFLINEDISABLE,
  SMARTEXEOFFIMMEDIATE, SMARTSHORTSELFTEST, SMARTEXTENDSELFTEST, 
  SMARTSHORTCAPSELFTEST, SMARTEXTENDCAPSELFTEST, SMARTSELFTESTABORT,
127
  SMARTAUTOSAVEENABLE,SMARTAUTOSAVEDISABLE,PRINTCOPYLEFT,SMART009MINUTES,
ballen4705's avatar
ballen4705 committed
128
129
130
  QUIETMODE,VERYQUIETMODE,NOTSCSIDEVICE,NOTATADEVICE,
  EXITCHECKSUMERROR,ULTRACONSERVATIVE,PERMISSIVE,
  'h','?','\0'
131
};
ballen4705's avatar
ballen4705 committed
132

133
unsigned char printcopyleft=0,tryata=0,tryscsi=0;
134

135
/*      Takes command options and sets features to be run */	
ballen4705's avatar
ballen4705 committed
136
void ParseOpts (int argc, char** argv){
137
138
139
  int optchar;
  extern char *optarg;
  extern int optopt, optind, opterr;
ballen4705's avatar
ballen4705 committed
140
  
141
142
  memset(con,0,sizeof(*con));
  con->testcase=-1;
143
  opterr=optopt=0;
144
  while (-1 != (optchar = getopt(argc, argv, opts))) {
ballen4705's avatar
ballen4705 committed
145
    switch (optchar){
ballen4705's avatar
ballen4705 committed
146
147
148
149
150
151
152
153
154
    case EXITCHECKSUMERROR:
      con->checksumfail=1;
      break;
    case PERMISSIVE:
      con->permissive=1;
      break;
    case ULTRACONSERVATIVE:
      con->conservative=1;
      break;
155
156
157
158
159
160
161
162
    case NOTATADEVICE:
      tryata=0;
      tryscsi=1;
      break;
    case NOTSCSIDEVICE:
      tryata=1;
      tryscsi=0;
      break;
163
    case QUIETMODE:
164
      con->quietmode=TRUE;
165
166
      break;
    case VERYQUIETMODE:
167
      con->veryquietmode=TRUE;
168
      break;
169
    case SMART009MINUTES:
170
      con->smart009minutes=TRUE;
171
      break;
ballen4705's avatar
ballen4705 committed
172
173
174
175
    case PRINTCOPYLEFT :
      printcopyleft=TRUE;
      break;
    case DRIVEINFO :
176
      con->driveinfo  = TRUE;
ballen4705's avatar
ballen4705 committed
177
178
      break;		
    case CHECKSMART :
179
      con->checksmart = TRUE;		
ballen4705's avatar
ballen4705 committed
180
181
      break;
    case SMARTVERBOSEALL :
182
183
184
185
186
187
      con->driveinfo = TRUE;
      con->checksmart = TRUE;
      con->generalsmartvalues = TRUE;
      con->smartvendorattrib = TRUE;
      con->smarterrorlog = TRUE;
      con->smartselftestlog = TRUE;
ballen4705's avatar
ballen4705 committed
188
189
      break;
    case SMARTVENDORATTRIB :
190
      con->smartvendorattrib = TRUE;
ballen4705's avatar
ballen4705 committed
191
192
      break;
    case GENERALSMARTVALUES :
193
      con->generalsmartvalues = TRUE;
ballen4705's avatar
ballen4705 committed
194
195
      break;
    case SMARTERRORLOG :
196
      con->smarterrorlog = TRUE;
ballen4705's avatar
ballen4705 committed
197
198
      break;
    case SMARTSELFTESTLOG :
199
      con->smartselftestlog = TRUE;
ballen4705's avatar
ballen4705 committed
200
201
      break;
    case SMARTDISABLE :
202
      con->smartdisable = TRUE;
ballen4705's avatar
ballen4705 committed
203
204
      break;
    case SMARTENABLE :
205
      con->smartenable   = TRUE;
ballen4705's avatar
ballen4705 committed
206
207
      break;
    case SMARTAUTOSAVEENABLE:
208
      con->smartautosaveenable = TRUE;
ballen4705's avatar
ballen4705 committed
209
210
      break;
    case SMARTAUTOSAVEDISABLE:
211
      con->smartautosavedisable = TRUE;
ballen4705's avatar
ballen4705 committed
212
213
      break;
    case SMARTAUTOOFFLINEENABLE: 
214
      con->smartautoofflineenable = TRUE;
ballen4705's avatar
ballen4705 committed
215
216
      break;
    case SMARTAUTOOFFLINEDISABLE:
217
      con->smartautoofflinedisable = TRUE;
ballen4705's avatar
ballen4705 committed
218
219
      break;
    case SMARTEXEOFFIMMEDIATE:
220
221
      con->smartexeoffimmediate = TRUE;
      con->testcase=OFFLINE_FULL_SCAN;
ballen4705's avatar
ballen4705 committed
222
223
      break;
    case SMARTSHORTSELFTEST :
224
225
      con->smartshortselftest = TRUE;
      con->testcase=SHORT_SELF_TEST;
ballen4705's avatar
ballen4705 committed
226
227
      break;
    case SMARTEXTENDSELFTEST :
228
229
      con->smartextendselftest = TRUE;
      con->testcase=EXTEND_SELF_TEST;
ballen4705's avatar
ballen4705 committed
230
231
      break;
    case SMARTSHORTCAPSELFTEST:
232
233
      con->smartshortcapselftest = TRUE;
      con->testcase=SHORT_CAPTIVE_SELF_TEST;
ballen4705's avatar
ballen4705 committed
234
235
      break;
    case SMARTEXTENDCAPSELFTEST:
236
237
      con->smartextendcapselftest = TRUE;
      con->testcase=EXTEND_CAPTIVE_SELF_TEST;
ballen4705's avatar
ballen4705 committed
238
239
      break;
    case SMARTSELFTESTABORT:
240
241
      con->smartselftestabort = TRUE;
      con->testcase=ABORT_SELF_TEST;
ballen4705's avatar
ballen4705 committed
242
      break;
243
244
    case 'h':
    case '?':
ballen4705's avatar
ballen4705 committed
245
    default:
246
      con->veryquietmode=FALSE;
247
      printslogan();
248
      if (optopt){
ballen4705's avatar
ballen4705 committed
249
	pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",(int)optopt);
250
251
252
	Usage();
	exit(FAILCMD);
      }
ballen4705's avatar
ballen4705 committed
253
      Usage();
254
      exit(0);	
ballen4705's avatar
ballen4705 committed
255
256
    }
  }
257
  // Do this here, so results are independent of argument order	
258
259
  if (con->quietmode)
    con->veryquietmode=TRUE;
260
  
261
  // error message if user has asked for more than one test
262
263
264
  if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+
	 con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort)){
    con->veryquietmode=FALSE;
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
    printslogan();
    Usage();
    printf ("\nERROR: smartctl can only run a single test (or abort) at a time.\n\n");
    exit(FAILCMD);
  }

  // From here on, normal operations...
  printslogan();
  
  // Print Copyright/License info if needed
  if (printcopyleft){
    printcopy();
    if (argc==2)
      exit(0);
  }   
ballen4705's avatar
ballen4705 committed
280
281
282
}


ballen4705's avatar
ballen4705 committed
283
284
285
286
287
288
289
290
// Printing function (controlled by global con->veryquietmode) 

// [From GLIBC Manual: Since the prototype doesn't specify types for
// optional arguments, in a call to a variadic function the default
// argument promotions are performed on the optional argument
// values. This means the objects of type char or short int (whether
// signed or not) are promoted to either int or unsigned int, as
// appropriate.]
291
292
293
294
295
void pout(char *fmt, ...){
  va_list ap;

  // initialize variable argument list 
  va_start(ap,fmt);
296
  if (con->veryquietmode){
297
298
299
300
301
302
303
304
305
306
307
308
    va_end(ap);
    return;
  }

  // print out
  vprintf(fmt,ap);
  va_end(ap);
  return;
}


/* Main Program */
ballen4705's avatar
ballen4705 committed
309
int main (int argc, char **argv){
310
  int fd,retval=0;
ballen4705's avatar
ballen4705 committed
311
  char *device;
312
  atamainctrl control;
313
  const char *devroot="/dev/";
314
315
316
317

  // define control block for external functions
  con=&control;

ballen4705's avatar
ballen4705 committed
318
  // Part input arguments
319
  ParseOpts(argc,argv);
320

321
  // open device - read-only mode is enough to issue needed commands
322
  fd = open(device=argv[argc-1], O_RDONLY);  
323
  if (fd<0) {
324
325
326
327
    char errmsg[256];
    snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
    errmsg[255]='\0';
    syserror(errmsg);
328
    return FAILDEV;
ballen4705's avatar
ballen4705 committed
329
  }
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

  // if necessary, try to guess if this is an ATA or SCSI device
  if (!tryata && !tryscsi) {
    if (!strncmp(device,devroot,strlen(devroot)) && strlen(device)>5){
      if (device[5] == 'h')
	tryata=1;
      if (device[5] == 's')
	tryscsi=1;
    }
    else if (strlen(device)){
	if (device[0] == 'h')
	  tryata=1;
	if (device[0] == 's')
	  tryscsi=1;
      }
  }

  // now call appropriate ATA or SCSI routine
  if (tryata)
349
    retval=ataPrintMain(fd);
350
  else if (tryscsi)
351
    scsiPrintMain (device, fd);
352
  else {
353
354
    pout("Smartctl: specify if this is an ATA or SCSI device with the -%c or -%c options respectively.\n",
	 NOTSCSIDEVICE, NOTATADEVICE);
ballen4705's avatar
ballen4705 committed
355
    Usage();
356
357
358
359
    return FAILCMD;
  }

  return retval;
ballen4705's avatar
ballen4705 committed
360
}