/* * 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.45 2003/01/07 00:41:37 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("\n============== SHOW INFORMATION OPTIONS =================================\n"); #ifdef HAVE_GETOPT_LONG printf("\ -h, -?, --help, --usage\n\ Display this help and exit\n\ -V, --version, --copyright, --license\n\ Print license, copyright, and version information and exit\n\ -i, --info \n\ Show identity information for device\n\ -a, --all \n\ Show all SMART information for device\n\ "); #else printf("\ -h, -? Display this help and exit\n\ -V Print license, copyright, and version information\n\ -i Show identity information for device\n\ -a Show all SMART information for device \n\ "); #endif printf("============== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =======================\n"); #ifdef HAVE_GETOPT_LONG printf("\ -q TYPE, --quietmode=TYPE (ATA)\n\ Set smartctl quiet mode to one of: errorsonly, silent\n\ -d TYPE, --device=TYPE\n\ Specify device type to one of: ata, scsi\n\ -T TYPE, --tolerance=TYPE (ATA)\n\ Set tolerance to one of: normal, conservative, permissive\n\ -b TYPE, --badsum=TYPE (ATA)\n\ Set action on bad checksum to one of: warn, exit, ignore\n\ "); #else printf("\ -q TYPE Set smartctl quiet mode to one of: errorsonly, silent (ATA)\n\ -d TYPE Specify device type to one of: ata, scsi\n\ -T TYPE Set tolerance to one of: normal, conservative, permissive (ATA)\n\ -b TYPE Set action on bad checksum to one of: warn, exit, ignore (ATA)\n\ "); #endif printf("============== DEVICE FEATURE ENABLE/DISABLE COMMANDS ===================\n"); #ifdef HAVE_GETOPT_LONG printf("\ -s VALUE, --smart=VALUE\n\ Enable/disable SMART on device (on/off)\n\ -o VALUE, --offlineauto=VALUE (ATA)\n\ Enable/disable automatic offline testing on device (on/off)\n\ -S VALUE, --saveauto=VALUE (ATA)\n\ Enable/disable Attribute autosave on device (on/off)\n\ "); #else printf("\ -s VALUE Enable/disable SMART on device (on/off)\n\ -o VALUE Enable/disable device automatic offline testing (on/off) (ATA)\n\ -S VALUE Enable/disable device Attribute autosave (on/off) (ATA)\n\ "); #endif printf("============== READ AND DISPLAY DATA OPTIONS ============================\n"); #ifdef HAVE_GETOPT_LONG printf("\ -H, --health\n\ Show device SMART health status\n\ -c, --capabilities (ATA)\n\ Show device SMART capabilities\n\ -A, --attributes (ATA)\n\ Show device SMART vendor-specific Attributes and values\n\ -l TYPE, --log=TYPE\n\ Show device log. Type is one of: error (ATA), selftest\n\ -v N,OPTION , --vendorattribute=N,OPTION (ATA)\n\ Set display OPTION for vendor Attribute N (see man page)\n\ "); #else printf("\ -H Show device SMART health status\n\ -c Show device SMART capabilities (ATA)\n\ -A Show device SMART vendor-specific Attributes and values (ATA)\n\ -l TYPE Show device log. Type is one of: error (ATA), selftest\n\ -v N,OPT Set display OPTion for vendor Attribute N (see man page) (ATA)\n\ "); #endif printf("============== DEVICE SELF-TEST OPTIONS =================================\n"); #ifdef HAVE_GETOPT_LONG printf("\ -t TEST, --test=TEST\n\ Run test on device. TEST is one of: offline, short, long\n\ -C, --captive\n\ With -t, performs test in captive mode (short/long only)\n\ -X, --abort\n\ Abort any non-captive test on device\n\ "); #else printf("\ -t TEST Run test on device. TEST is one of: offline, short, long \n\ -C With -t, performs test in captive mode (short/long only) \n\ -X Abort any non-captive test \n\ "); #endif printf("============== SMARTCTL EXAMPLES ========================================\n"); #ifdef HAVE_GETOPT_LONG printf("\ smartctl -a /dev/hda (Prints all SMART information)\n\ smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n\ (Enables SMART on first disk)\n\ smartctl -t long /dev/hda (Executes extended disk self-test)\n\ smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n\ (Prints Self-Test & Attribute errors)\n\ "); #else printf("\ smartctl -a /dev/hda (Prints all SMART information)\n\ smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n\ smartctl -t long /dev/hda (Executes extended disk self-test)\n\ smartctl -A -l selftest -q errorsonly /dev/hda\n\ (Prints Self-Test & Attribute errors)\n\ "); #endif } /* Returns a pointer to a static string containing a formatted list of the valid arguments to the option opt or NULL on failure. */ const char *getvalidarglist(char opt) { static char *v_list = NULL; char *s; switch (opt) { case 'q': return "errorsonly, silent"; case 'd': return "ata, scsi"; case 'T': return "normal, conservative, permissive"; case 'b': return "warn, exit, ignore"; case 's': case 'o': case 'S': return "on, off"; case 'l': return "error, selftest"; case 'v': if (v_list) return v_list; if (!(s = create_vendor_attribute_arg_list())) return NULL; // Allocate space for "\"help\", " + s + terminating 0 v_list = (char *)malloc(9+strlen(s)); sprintf(v_list, "\"help\", %s", s); free(s); return v_list; case 't': return "offline, short, long"; default: return NULL; } } unsigned char 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; extern char *optarg; extern int optopt, optind, opterr; // Please update getvalidarglist() if you edit shortopts const char *shortopts = "h?Vq:d:T:b:s:o:S:HcAl:iav:t:CX"; #ifdef HAVE_GETOPT_LONG char *arg; // Please update getvalidarglist() if you edit longopts struct option longopts[] = { { "help", no_argument, 0, 'h' }, { "usage", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { "copyright", no_argument, 0, 'V' }, { "license", no_argument, 0, 'V' }, { "quietmode", required_argument, 0, 'q' }, { "device", required_argument, 0, 'd' }, { "tolerance", required_argument, 0, 'T' }, { "badsum", required_argument, 0, 'b' }, { "smart", required_argument, 0, 's' }, { "offlineauto", required_argument, 0, 'o' }, { "saveauto", required_argument, 0, 'S' }, { "health", no_argument, 0, 'H' }, { "capabilities", no_argument, 0, 'c' }, { "attributes", no_argument, 0, 'A' }, { "log", required_argument, 0, 'l' }, { "info", no_argument, 0, 'i' }, { "all", no_argument, 0, 'a' }, { "vendorattribute", required_argument, 0, 'v' }, { "test", required_argument, 0, 't' }, { "captive", no_argument, 0, 'C' }, { "abort", no_argument, 0, 'X' }, { 0, 0, 0, 0 } }; #endif memset(con,0,sizeof(*con)); con->testcase=-1; opterr=optopt=0; badarg = captive = FALSE; // This miserable construction is needed to get emacs to do proper indenting. Sorry! while (-1 != (optchar = #ifdef HAVE_GETOPT_LONG getopt_long(argc, argv, shortopts, longopts, NULL) #else getopt(argc, argv, shortopts) #endif )){ switch (optchar){ case 'V': con->veryquietmode=FALSE; printslogan(); printcopy(); exit(0); break; case 'q': 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 'd': if (!strcmp(optarg,"ata")) { tryata = TRUE; tryscsi = FALSE; } else if (!strcmp(optarg,"scsi")) { tryata = TRUE; tryscsi = FALSE; } else { badarg = TRUE; } break; case 'T': 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 'b': 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': 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 'o': 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': 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 'H': con->checksmart = TRUE; break; case 'c': con->generalsmartvalues = TRUE; break; case 'A': con->smartvendorattrib = TRUE; break; case 'l': if (!strcmp(optarg,"error")) { con->smarterrorlog = TRUE; } else if (!strcmp(optarg,"selftest")) { con->smartselftestlog = TRUE; } else { badarg = TRUE; } break; case 'i': con->driveinfo = TRUE; break; case 'a': con->driveinfo = TRUE; con->checksmart = TRUE; con->generalsmartvalues = TRUE; con->smartvendorattrib = TRUE; con->smarterrorlog = TRUE; con->smartselftestlog = TRUE; break; case 'v': // parse vendor-specific definitions of attributes if (!strcmp(optarg,"help")) { char *s; con->veryquietmode=FALSE; printslogan(); if (!(s = create_vendor_attribute_arg_list())) { pout("Insufficient memory to construct argument list\n"); exit(FAILCMD); } pout("The valid arguments to -v are: \"help\", %s\n", s); free(s); exit(0); } if (parse_attribute_def(optarg, con->attributedefs)) badarg = TRUE; break; case 't': 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 'C': captive = TRUE; break; case 'X': con->smartselftestabort = TRUE; con->testcase = ABORT_SELF_TEST; break; case 'h': case '?': 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 that doesn't map to -h. if (arg[1] == '-' && optchar != 'h') { // Iff optopt holds a valid option then argument must be missing. if (optopt && (strchr(shortopts, optopt) != NULL)) { pout("=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n", arg+2); pout("=======> VALID ARGUMENTS ARE: %s <=======\n", getvalidarglist(optopt)); } else pout("=======> UNRECOGNIZED OPTION: %s <=======\n",arg+2); pout("\nUse smartctl --help to get a usage summary\n\n"); 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", optopt); pout("=======> VALID ARGUMENTS ARE: %s <=======\n", getvalidarglist(optopt)); } else pout("=======> UNRECOGNIZED OPTION: %c <=======\n",optopt); pout("\nUse smartctl -h to get a usage summary\n\n"); exit(FAILCMD); } Usage(); exit(0); } // closes switch statement to process command-line options // Check to see if option had an unrecognized or incorrect argument. if (badarg) { printslogan(); // It would be nice to print the actual option name given by the user // here, but we just print the short form. Please fix this if you know // a clean way to do it. pout("=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg); pout("=======> VALID ARGUMENTS ARE: %s <=======\n", getvalidarglist(optchar)); pout("\nUse smartctl -h to get a usage summary\n\n"); exit(FAILCMD); } } // At this point we have processed all command-line options. // 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(); pout("\nERROR: smartctl can only run a single test (or abort) at a time.\n"); pout("Use smartctl -h to get a usage summary\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(); // Warn if the user has provided no device name if (argc-optind<1){ pout("ERROR: smartctl requires a device name as the final command-line argument.\n\n"); pout("Use smartctl -h to get a usage summary\n\n"); exit(FAILCMD); } // Warn if the user has provided more than one device name if (argc-optind>1){ int i; pout("ERROR: smartctl takes ONE device name as the final command-line argument.\n"); pout("You have provided %d device names:\n",argc-optind); for (i=0; i<argc-optind; i++) pout("%s\n",argv[optind+i]); pout("Use smartctl -h to get a usage summary\n\n"); exit(FAILCMD); } } // 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 -d option.\n"); Usage(); return FAILCMD; } return retval; }