/* * smartctl.c * * Home page of code is: http://smartmontools.sourceforge.net * * Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net> * 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. * * 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/ * */ #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> #include <stdarg.h> #ifdef HAVE_GETOPT_H #include <getopt.h> #endif #include "smartctl.h" #include "atacmds.h" #include "ataprint.h" #include "scsicmds.h" #include "scsiprint.h" #include "extern.h" extern const char *CVSid1, *CVSid2, *CVSid3, *CVSid4; const char* CVSid5="$Id: smartctl.cpp,v 1.30 2002/12/11 00:11:31 pjwilliams Exp $" CVSID1 CVSID2 CVSID3 CVSID4 CVSID5 CVSID6; // This is a block containing all the "control variables". We declare // this globally in this file, and externally in other files. atamainctrl *con=NULL; void printslogan(){ pout("smartctl version %d.%d-%d Copyright (C) 2002 Bruce Allen\n", (int)RELEASE_MAJOR, (int)RELEASE_MINOR, (int)SMARTMONTOOLS_VERSION); pout("Home page is %s\n\n",PROJECTHOME); return; } void printcopy(){ char out[CVSMAXLEN]; 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"); printone(out,CVSid1); pout("%s",out); printone(out,CVSid2); pout("%s",out); printone(out,CVSid3); pout("%s",out); printone(out,CVSid4); pout("%s",out); printone(out,CVSid5); pout("%s",out); return; } /* void prints help information for command syntax */ void Usage ( void){ printf("Usage: smartctl -[options] [device]\n"); printf("\nShow Information Options:\n"); printf(" %c Show Version, Copyright and License info\n", PRINTCOPYLEFT); printf(" %c Show SMART Drive Info (ATA/SCSI)\n",DRIVEINFO); 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); 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); 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"); 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 ); printf("\nExamples:\n"); 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"); } const char shortopts[] = { S_OPT_HELP, S_OPT_ALT_HELP, S_OPT_VERSION, S_OPT_QUIETMODE, ':', S_OPT_DEVICE, ':', S_OPT_TOLERANCE, ':', S_OPT_BADSUM, ':', S_OPT_SMART, ':', S_OPT_OFFLINEAUTO, ':', S_OPT_SAVEAUTO, ':', S_OPT_HEALTH, S_OPT_CAPABILITIES, S_OPT_ATTRIBUTES, S_OPT_LOG, ':', S_OPT_INFO, S_OPT_ALL, S_OPT_VENDORATTRIBUTE, ':', S_OPT_TEST, ':', S_OPT_CAPTIVE, S_OPT_ABORT, '\0' }; unsigned char printcopyleft=0,tryata=0,tryscsi=0; /* Takes command options and sets features to be run */ void ParseOpts (int argc, char** argv){ int optchar; int badarg; int captive; struct { int n; char *option; } vendorattribute; extern char *optarg; extern int optopt, optind, opterr; #ifdef HAVE_GETOPT_LONG char *arg; struct option longopts[] = { { L_OPT_HELP, no_argument, 0, S_OPT_HELP }, { L_OPT_USAGE, no_argument, 0, S_OPT_HELP }, { L_OPT_VERSION, no_argument, 0, S_OPT_VERSION }, { L_OPT_COPYRIGHT, no_argument, 0, S_OPT_VERSION }, { L_OPT_LICENSE, no_argument, 0, S_OPT_VERSION }, { L_OPT_QUIETMODE, required_argument, 0, S_OPT_QUIETMODE }, { L_OPT_DEVICE, required_argument, 0, S_OPT_DEVICE }, { L_OPT_TOLERANCE, required_argument, 0, S_OPT_TOLERANCE }, { L_OPT_BADSUM, required_argument, 0, S_OPT_BADSUM }, { L_OPT_SMART, required_argument, 0, S_OPT_SMART }, { L_OPT_OFFLINEAUTO, required_argument, 0, S_OPT_OFFLINEAUTO }, { L_OPT_SAVEAUTO, required_argument, 0, S_OPT_SAVEAUTO }, { L_OPT_HEALTH, no_argument, 0, S_OPT_HEALTH }, { L_OPT_CAPABILITIES, no_argument, 0, S_OPT_CAPABILITIES }, { L_OPT_ATTRIBUTES, no_argument, 0, S_OPT_ATTRIBUTES }, { L_OPT_LOG, required_argument, 0, S_OPT_LOG }, { L_OPT_INFO, no_argument, 0, S_OPT_INFO }, { L_OPT_ALL, no_argument, 0, S_OPT_ALL }, { L_OPT_VENDORATTRIBUTE, required_argument, 0, S_OPT_VENDORATTRIBUTE }, { L_OPT_TEST, required_argument, 0, S_OPT_TEST }, { L_OPT_CAPTIVE, no_argument, 0, S_OPT_CAPTIVE }, { L_OPT_ABORT, no_argument, 0, S_OPT_ABORT }, { 0, 0, 0, 0 } }; #endif memset(con,0,sizeof(*con)); con->testcase=-1; opterr=optopt=0; badarg = captive = FALSE; #ifdef HAVE_GETOPT_LONG while (-1 != (optchar = getopt_long(argc, argv, shortopts, longopts, NULL))) { #else while (-1 != (optchar = getopt(argc, argv, shortopts))) { #endif switch (optchar){ case S_OPT_VERSION: printcopyleft=TRUE; break; case S_OPT_QUIETMODE: if (!strcmp(optarg,"errorsonly")) { con->quietmode = TRUE; con->veryquietmode = FALSE; } else if (!strcmp(optarg,"silent")) { con->veryquietmode = TRUE; con->quietmode = TRUE; } else { badarg = TRUE; } break; case S_OPT_DEVICE: if (!strcmp(optarg,"ata")) { tryata = TRUE; tryscsi = FALSE; } else if (!strcmp(optarg,"scsi")) { tryata = TRUE; tryscsi = FALSE; } else { badarg = TRUE; } break; case S_OPT_TOLERANCE: if (!strcmp(optarg,"normal")) { con->conservative = FALSE; con->permissive = FALSE; } else if (!strcmp(optarg,"conservative")) { con->conservative = TRUE; con->permissive = FALSE; } else if (!strcmp(optarg,"permissive")) { con->permissive = TRUE; con->conservative = FALSE; } else { badarg = TRUE; } break; case S_OPT_BADSUM: if (!strcmp(optarg,"warn")) { con->checksumfail = FALSE; con->checksumignore = FALSE; } else if (!strcmp(optarg,"exit")) { con->checksumfail = TRUE; con->checksumignore = FALSE; } else if (!strcmp(optarg,"ignore")) { con->checksumignore = TRUE; con->checksumfail = FALSE; } else { badarg = TRUE; } break; case S_OPT_SMART: if (!strcmp(optarg,"on")) { con->smartenable = TRUE; con->smartdisable = FALSE; } else if (!strcmp(optarg,"off")) { con->smartdisable = TRUE; con->smartenable = FALSE; } else { badarg = TRUE; } break; case S_OPT_OFFLINEAUTO: if (!strcmp(optarg,"on")) { con->smartautoofflineenable = TRUE; con->smartautoofflinedisable = FALSE; } else if (!strcmp(optarg,"off")) { con->smartautoofflinedisable = TRUE; con->smartautoofflineenable = FALSE; } else { badarg = TRUE; } break; case S_OPT_SAVEAUTO: if (!strcmp(optarg,"on")) { con->smartautosaveenable = TRUE; con->smartautosavedisable = FALSE; } else if (!strcmp(optarg,"off")) { con->smartautosavedisable = TRUE; con->smartautosaveenable = FALSE; } else { badarg = TRUE; } break; case S_OPT_HEALTH: con->checksmart = TRUE; break; case S_OPT_CAPABILITIES: con->generalsmartvalues = TRUE; break; case S_OPT_ATTRIBUTES: con->smartvendorattrib = TRUE; break; case S_OPT_LOG: if (!strcmp(optarg,"error")) { con->smarterrorlog = TRUE; } else if (!strcmp(optarg,"selftest")) { con->smartselftestlog = TRUE; } else { badarg = TRUE; } break; case S_OPT_INFO: con->driveinfo = TRUE; break; case S_OPT_ALL: con->driveinfo = TRUE; con->checksmart = TRUE; con->generalsmartvalues = TRUE; con->smartvendorattrib = TRUE; con->smarterrorlog = TRUE; con->smartselftestlog = TRUE; break; case S_OPT_VENDORATTRIBUTE: vendorattribute.option = (char *)malloc(strlen(optarg)+1); if (sscanf(optarg,"%u,%s",&(vendorattribute.n),vendorattribute.option) != 2) { badarg = TRUE; } if (vendorattribute.n == 9 && !strcmp(vendorattribute.option,"minutes")) { con->smart009minutes=TRUE; } else { // Should handle this better badarg = TRUE; } free(vendorattribute.option); break; case S_OPT_TEST: if (!strcmp(optarg,"offline")) { con->smartexeoffimmediate = TRUE; con->testcase = OFFLINE_FULL_SCAN; } else if (!strcmp(optarg,"short")) { con->smartshortselftest = TRUE; con->testcase = SHORT_SELF_TEST; } else if (!strcmp(optarg,"long")) { con->smartextendselftest = TRUE; con->testcase = EXTEND_SELF_TEST; } else { badarg = TRUE; } break; case S_OPT_CAPTIVE: captive = TRUE; break; case S_OPT_ABORT: con->smartselftestabort = TRUE; con->testcase = ABORT_SELF_TEST; break; case S_OPT_HELP: case S_OPT_ALT_HELP: default: con->veryquietmode=FALSE; printslogan(); #ifdef HAVE_GETOPT_LONG // Point arg to the argument in which this option was found. arg = argv[optind-1]; // Check whether the option is a long option and options that map to -h. if (arg[1] == '-' && optchar != S_OPT_HELP) { // Iff optopt holds a valid option then argument must be missing. if (optopt && (strchr(shortopts, optopt) != NULL)) { pout("=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n\n",arg+2); } else { pout("=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2); } Usage(); exit(FAILCMD); } #endif if (optopt) { // Iff optopt holds a valid option then argument must be missing. if (strchr(shortopts, optopt) != NULL){ pout("=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n\n",optopt); } else { pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt); } Usage(); exit(FAILCMD); } Usage(); exit(0); } if (badarg) { pout("=======> INVALID ARGUMENT: %s <======= \n\n",optarg); Usage(); exit(FAILCMD); } } // Do this here, so results are independent of argument order if (con->quietmode) con->veryquietmode=TRUE; // error message if user has asked for more than one test if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+ con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort)){ con->veryquietmode=FALSE; printslogan(); Usage(); printf ("\nERROR: smartctl can only run a single test (or abort) at a time.\n\n"); exit(FAILCMD); } // If captive option was used, change test type if appropriate. if (captive && con->smartshortselftest) { con->smartshortselftest = FALSE; con->smartshortcapselftest = TRUE; con->testcase = SHORT_CAPTIVE_SELF_TEST; } else if (captive && con->smartextendselftest) { con->smartextendselftest = FALSE; con->smartextendcapselftest = TRUE; con->testcase = EXTEND_CAPTIVE_SELF_TEST; } // From here on, normal operations... printslogan(); // Print Copyright/License info if needed if (printcopyleft){ printcopy(); if (argc==2) exit(0); } } // 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.] void pout(char *fmt, ...){ va_list ap; // initialize variable argument list va_start(ap,fmt); if (con->veryquietmode){ va_end(ap); return; } // print out vprintf(fmt,ap); va_end(ap); return; } /* Main Program */ int main (int argc, char **argv){ int fd,retval=0; char *device; atamainctrl control; const char *devroot="/dev/"; // define control block for external functions con=&control; // Part input arguments ParseOpts(argc,argv); // open device - read-only mode is enough to issue needed commands fd = open(device=argv[argc-1], O_RDONLY); if (fd<0) { char errmsg[256]; snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]); errmsg[255]='\0'; syserror(errmsg); return FAILDEV; } // 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) retval=ataPrintMain(fd); else if (tryscsi) scsiPrintMain (device, fd); else { pout("Smartctl: specify if this is an ATA or SCSI device with the -%c or -%c options respectively.\n", NOTSCSIDEVICE, NOTATADEVICE); Usage(); return FAILCMD; } return retval; }