]> git.proxmox.com Git - mirror_smartmontools-debian.git/blobdiff - smartctl.cpp
typo
[mirror_smartmontools-debian.git] / smartctl.cpp
index cb0d3dbf85d6169fe418c4bf3f8877b0aaf01998..7da7a0b1c5adc2b4042571ad362a77901a78eac4 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-7 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <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
 #include <sys/types.h>
 #include <string.h>
 #include <stdarg.h>
+#include <stdexcept>
+#include <getopt.h>
 
 #include "config.h"
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
-#endif
-#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000)
+
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
-#if defined(__QNXNTO__) 
-#include <unistd.h>
+#if defined(__FreeBSD__)
+#include <sys/param.h>
 #endif
 
+#if defined(__QNXNTO__) 
+#include <new> // TODO: Why is this include necessary on QNX ?
+#endif
 
 #include "int64.h"
 #include "atacmds.h"
+#include "dev_interface.h"
 #include "ataprint.h"
 #include "extern.h"
 #include "knowndrives.h"
 #include "smartctl.h"
 #include "utility.h"
 
-#ifdef NEED_SOLARIS_ATA_CODE
-extern const char *os_solaris_ata_s_cvsid;
-#endif
-extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
-const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.168 2007/11/13 14:53:27 jhering Exp $"
-ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 2915 2009-09-18 21:17:37Z chrfranke $"
+                                  CONFIG_H_CVSID EXTERN_H_CVSID SMARTCTL_H_CVSID;
 
 // This is a block containing all the "control variables".  We declare
 // this globally in this file, and externally in other files.
 smartmonctrl *con=NULL;
 
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-// Track memory use
-extern int64_t bytes;
-
-void printslogan(){
-#ifdef HAVE_GET_OS_VERSION_STR
-  const char * ver = get_os_version_str();
-#else
-  const char * ver = SMARTMONTOOLS_BUILD_HOST;
-#endif
-  pout("smartctl version %s [%s] Copyright (C) 2002-7 Bruce Allen\n", PACKAGE_VERSION, ver);
-  pout("Home page is " PACKAGE_HOMEPAGE "\n\n");
-  return;
-}
-
-void PrintOneCVS(const char *a_cvs_id){
-  char out[CVSMAXLEN];
-  printone(out,a_cvs_id);
-  pout("%s",out);
-  return;
-}
-
-void printcopy(){
-  const char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
-
-  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");
-  PrintOneCVS(atacmdnames_c_cvsid);
-  PrintOneCVS(atacmds_c_cvsid);
-  PrintOneCVS(ataprint_c_cvsid);
-  PrintOneCVS(knowndrives_c_cvsid);
-  PrintOneCVS(os_XXXX_c_cvsid);
-#ifdef NEED_SOLARIS_ATA_CODE
-  PrintOneCVS(os_solaris_ata_s_cvsid);
-#endif
-  PrintOneCVS(scsicmds_c_cvsid);
-  PrintOneCVS(scsiprint_c_cvsid);
-  PrintOneCVS(smartctl_c_cvsid);
-  PrintOneCVS(utility_c_cvsid);
-  pout("\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
-  pout("smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
-  pout("smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
-  pout("smartctl compile dated " __DATE__ " at "__TIME__ "\n");
-  pout("smartmontools configure arguments: %s\n", configargs);
-  return;
+static void printslogan()
+{
+  pout("%s\n", format_version_info("smartctl").c_str());
 }
 
 void UsageSummary(){
@@ -119,12 +73,13 @@ void UsageSummary(){
   return;
 }
 
+static std::string getvalidarglist(char opt);
+
 /*  void prints help information for command syntax */
 void Usage (void){
   printf("Usage: smartctl [options] device\n\n");
-  printf("============================================ SHOW INFORMATION OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
   printf(
+"============================================ SHOW INFORMATION OPTIONS =====\n\n"
 "  -h, --help, --usage\n"
 "         Display this help and exit\n\n"
 "  -V, --version, --copyright, --license\n"
@@ -133,22 +88,15 @@ void Usage (void){
 "         Show identity information for device\n\n"
 "  -a, --all                                                        \n"
 "         Show all SMART information for device\n\n"
+"  -x, --xall\n"
+"         Show all information for device\n\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\n"
-  );
-#endif
-  printf("================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
   printf(
+"================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n"
 "  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
 "         Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n"
 "  -d TYPE, --device=TYPE\n"
-"         Specify device type to one of: ata, scsi, marvell, sat, 3ware,N\n\n"
+"         Specify device type to one of: %s\n\n"
 "  -T TYPE, --tolerance=TYPE                                           (ATA)\n"
 "         Tolerance: normal, conservative, permissive, verypermissive\n\n"
 "  -b TYPE, --badsum=TYPE                                              (ATA)\n"
@@ -156,22 +104,10 @@ void Usage (void){
 "  -r TYPE, --report=TYPE\n"
 "         Report transactions (see man page)\n\n"
 "  -n MODE, --nocheck=MODE                                             (ATA)\n"
-"         No check if: never, sleep, standby, idle (see man page)\n\n"
-  );
-#else
-  printf(
-"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent,    (ATA)\n"
-"                                               noserial\n"
-"  -d TYPE   Specify device type to one of: ata, scsi, 3ware,N\n"
-"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA)\n"
-"  -b TYPE   Set action on bad checksum to one of: warn, exit, ignore  (ATA)\n"
-"  -r TYPE   Report transactions (see man page)\n"
-"  -n MODE   No check if: never, sleep, standby, idle (see man page)   (ATA)\n\n"
-  );
-#endif
-  printf("============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
+"         No check if: never, sleep, standby, idle (see man page)\n\n",
+  getvalidarglist('d').c_str()); // TODO: Use this function also for other options ?
   printf(
+"============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n"
 "  -s VALUE, --smart=VALUE\n"
 "        Enable/disable SMART on device (on/off)\n\n"
 "  -o VALUE, --offlineauto=VALUE                                       (ATA)\n"
@@ -179,16 +115,8 @@ void Usage (void){
 "  -S VALUE, --saveauto=VALUE                                          (ATA)\n"
 "        Enable/disable Attribute autosave on device (on/off)\n\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\n"
-  );
-#endif
-  printf("======================================= READ AND DISPLAY DATA OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
   printf(
+"======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"
 "  -H, --health\n"
 "        Show device SMART health status\n\n"
 "  -c, --capabilities                                                  (ATA)\n"
@@ -196,8 +124,11 @@ void Usage (void){
 "  -A, --attributes                                                         \n"
 "        Show device SMART vendor-specific Attributes and values\n\n"
 "  -l TYPE, --log=TYPE\n"
-"        Show device log. TYPE: error, selftest, selective, directory,\n"
-"                               background, scttemp[sts,hist]\n\n"
+"        Show device log. TYPE: error, selftest, selective, directory[,g|s],\n"
+"                               background, sasphy[,reset], sataphy[,reset],\n"
+"                               scttemp[sts,hist],\n"
+"                               gplog,N[,RANGE], smartlog,N[,RANGE],\n"
+"                               xerror[,N][,error], xselftest[,N][,selftest]\n\n"
 "  -v N,OPTION , --vendorattribute=N,OPTION                            (ATA)\n"
 "        Set display OPTION for vendor Attribute N (see man page)\n\n"
 "  -F TYPE, --firmwarebug=TYPE                                         (ATA)\n"
@@ -205,23 +136,15 @@ void Usage (void){
 "                                     samsung3, swapid\n\n"
 "  -P TYPE, --presets=TYPE                                             (ATA)\n"
 "        Drive-specific presets: use, ignore, show, showall\n\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: error, selftest, selective, directory,\n"
-"                                   background, scttemp[sts,hist]\n"
-"  -v N,OPT  Set display OPTion for vendor Attribute N (see man page)   (ATA)\n"
-"  -F TYPE   Use firmware bug workaround: none, samsung, samsung2,      (ATA)\n"
-"                                         samsung3, swapid\n"
-"  -P TYPE   Drive-specific presets: use, ignore, show, showall         (ATA)\n\n"
-  );
+"  -B [+]FILE, --drivedb=[+]FILE                                       (ATA)\n"
+"        Read and replace [add] drive database from FILE\n"
+#ifdef SMARTMONTOOLS_DRIVEDBDIR
+"        [default is "SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h]\n"
 #endif
-  printf("============================================ DEVICE SELF-TEST OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
+"\n"
+  );
   printf(
+"============================================ DEVICE SELF-TEST OPTIONS =====\n\n"
 "  -t TEST, --test=TEST\n"
 "        Run test. TEST: offline short long conveyance select,M-N\n"
 "                        pending,N afterselect,[on|off] scttempint,N[,p]\n\n"
@@ -230,26 +153,20 @@ void Usage (void){
 "  -X, --abort\n"
 "        Abort any non-captive test on device\n\n"
 );
-#else
-  printf(
-"  -t TEST   Run test. TEST: offline short long conveyance select,M-N\n"
-"                            pending,N afterselect,[on|off] scttempint,N[,p]\n"
-"  -C        Do test in captive mode (along with -t)\n"
-"  -X        Abort any non-captive test\n\n"
-  );
-#endif
-  print_smartctl_examples();
-  return;
+  std::string examples = smi()->get_app_examples("smartctl");
+  if (!examples.empty())
+    printf("%s\n", examples.c_str());
 }
 
-/* Returns a pointer to a static string containing a formatted list of the valid
-   arguments to the option opt or NULL on failure. Note 'v' case different */
-const char *getvalidarglist(char opt) {
+/* Returns a string containing a formatted list of the valid arguments
+   to the option opt or empty on failure. Note 'v' case different */
+static std::string getvalidarglist(char opt)
+{
   switch (opt) {
   case 'q':
     return "errorsonly, silent, noserial";
   case 'd':
-    return "ata, scsi, marvell, sat, 3ware,N, hpt,L/M/N cciss,N";
+    return smi()->get_valid_dev_types_str() + ", test";
   case 'T':
     return "normal, conservative, permissive, verypermissive";
   case 'b':
@@ -261,7 +178,9 @@ const char *getvalidarglist(char opt) {
   case 'S':
     return "on, off";
   case 'l':
-    return "error, selftest, selective, directory, background, scttemp[sts|hist]";
+    return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], "
+           "sasphy[,reset], sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], "
+          "xerror[,N][,error], xselftest[,N][,selftest]";
   case 'P':
     return "use, ignore, show, showall";
   case 't':
@@ -272,52 +191,43 @@ const char *getvalidarglist(char opt) {
     return "never, sleep, standby, idle";
   case 'v':
   default:
-    return NULL;
+    return "";
   }
 }
 
 /* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> \n", where
    <LIST> is the list of valid arguments for option opt. */
 void printvalidarglistmessage(char opt) {
-  char *s;
-  
-  if (opt=='v')
-    s=create_vendor_attribute_arg_list();
-  else
-    s=(char *)getvalidarglist(opt);
-  
-  if (!s) {
-    pout("Error whilst constructing argument list for option %c", opt);
-    return;
-  }
  
   if (opt=='v'){
-    pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", s);
-    free(s);
+    pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n",
+         create_vendor_attribute_arg_list().c_str());
   }
   else {
   // getvalidarglist() might produce a multiline or single line string.  We
   // need to figure out which to get the formatting right.
-    char separator = strchr(s, '\n') ? '\n' : ' ';
-    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, (char *)s, separator);
+    std::string s = getvalidarglist(opt);
+    char separator = strchr(s.c_str(), '\n') ? '\n' : ' ';
+    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, s.c_str(), separator);
   }
 
   return;
 }
 
+// Checksum error mode
+enum checksum_err_mode_t {
+  CHECKSUM_ERR_WARN, CHECKSUM_ERR_EXIT, CHECKSUM_ERR_IGNORE
+};
+
+static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN;
+
 /*      Takes command options and sets features to be run */    
-void ParseOpts (int argc, char** argv){
-  int optchar;
-  int badarg;
-  int captive;
-  unsigned char *charp;
-  extern char *optarg;
-  extern int optopt, optind, opterr;
-  char extraerror[256];
+const char * parse_options(int argc, char** argv,
+                           ata_print_options & ataopts,
+                           scsi_print_options & scsiopts)
+{
   // Please update getvalidarglist() if you edit shortopts
-  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iav:P:t:CXF:n:";
-#ifdef HAVE_GETOPT_LONG
-  char *arg;
+  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:";
   // Please update getvalidarglist() if you edit longopts
   struct option longopts[] = {
     { "help",            no_argument,       0, 'h' },
@@ -339,6 +249,7 @@ void ParseOpts (int argc, char** argv){
     { "log",             required_argument, 0, 'l' },
     { "info",            no_argument,       0, 'i' },
     { "all",             no_argument,       0, 'a' },
+    { "xall",            no_argument,       0, 'x' },
     { "vendorattribute", required_argument, 0, 'v' },
     { "presets",         required_argument, 0, 'P' },
     { "test",            required_argument, 0, 't' },
@@ -346,188 +257,73 @@ void ParseOpts (int argc, char** argv){
     { "abort",           no_argument,       0, 'X' },
     { "firmwarebug",     required_argument, 0, 'F' },
     { "nocheck",         required_argument, 0, 'n' },
+    { "drivedb",         required_argument, 0, 'B' },
     { 0,                 0,                 0, 0   }
   };
-#endif
-  
+
+  char extraerror[256];
   memset(extraerror, 0, sizeof(extraerror));
   memset(con,0,sizeof(*con));
-  con->testcase=-1;
   opterr=optopt=0;
-  badarg = captive = FALSE;
-  
+
+  const char * type = 0; // set to -d optarg
+  bool no_defaultdb = false; // set true on '-B FILE'
+  bool badarg = false, captive = false;
+  int testcnt = 0; // number of self-tests requested
+
+  int optchar;
+  char *arg;
+
   // 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->dont_print=FALSE;
-      printslogan();
-      printcopy();
-      exit(0);
+      con->dont_print = false;
+      pout("%s", format_version_info("smartctl", true /*full*/).c_str());
+      EXIT(0);
       break;
     case 'q':
       if (!strcmp(optarg,"errorsonly")) {
-        con->printing_switchable     = TRUE;
-        con->dont_print = FALSE;
+        con->printing_switchable = true;
+        con->dont_print = false;
       } else if (!strcmp(optarg,"silent")) {
-        con->printing_switchable     = FALSE;
-        con->dont_print = TRUE;
+        con->printing_switchable = false;
+        con->dont_print = true;
       } else if (!strcmp(optarg,"noserial")) {
-        con->dont_print_serial = TRUE;
+        con->dont_print_serial = true;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'd':
-      con->controller_explicit = 1;
-      if (!strcmp(optarg,"ata")) {
-        con->controller_type = CONTROLLER_ATA;
-        con->controller_port = 0;
-      } else if (!strcmp(optarg,"scsi")) {
-        con->controller_type = CONTROLLER_SCSI;
-        con->controller_port = 0;
-      } else if (!strcmp(optarg,"marvell")) {
-        con->controller_type = CONTROLLER_MARVELL_SATA;
-        con->controller_port = 0;
-      } else if (!strncmp(optarg, "sat", 3)) {
-        con->controller_type = CONTROLLER_SAT;
-        con->controller_port = 0;
-        con->satpassthrulen = 0;
-        if (strlen(optarg) > 3) {
-          int k;
-          char * cp;
-
-          cp = strchr(optarg, ',');
-          if (cp && (1 == sscanf(cp + 1, "%d", &k)) &&
-              ((0 == k) || (12 == k) || (16 == k)))
-            con->satpassthrulen = k;
-          else {
-            sprintf(extraerror, "Option '-d sat,<n>' requires <n> to be "
-                    "0, 12 or 16\n");
-            badarg = TRUE;
-          }
-        }
-      } else if (!strncmp(optarg, "hpt", 3)){
-        unsigned char i, slash = 0;
-        con->hpt_data[0] = 0;
-        con->hpt_data[1] = 0;
-        con->hpt_data[2] = 0;
-        con->controller_type = CONTROLLER_HPT;
-        for (i=4; i < strlen(optarg); i++) {
-          if(optarg[i] == '/') {
-            slash++;
-            if(slash == 3) {
-              sprintf(extraerror, "Option '-d hpt,L/M/N' supports 2-3 items\n");
-              badarg = TRUE;
-              break;
-            }
-          }
-          else if ((optarg[i])>='0' && (optarg[i])<='9') {
-            if (con->hpt_data[slash]>1) { /* hpt_data[x] max 19 */
-              badarg = TRUE;
-              break;
-            }
-            con->hpt_data[slash] = con->hpt_data[slash]*10 + optarg[i] - '0';
-          }
-          else {
-            badarg = TRUE;
-            break;
-          }
-        }
-        if (slash == 0) {
-          sprintf(extraerror, "Option '-d hpt,L/M/N' requires 2-3 items\n");
-          badarg = TRUE;
-        } else if (badarg != TRUE) {
-          if (con->hpt_data[0]==0 || con->hpt_data[0]>8){
-            sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid controller id L supplied\n");
-            badarg = TRUE;
-          }
-          if (con->hpt_data[1]==0 || con->hpt_data[1]>8){
-            sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid channel number M supplied\n");
-            badarg = TRUE;
-          }
-          if (slash==2) {
-            if ( con->hpt_data[2]==0 || con->hpt_data[2]>15) {
-              sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid pmport number N supplied\n");
-              badarg = TRUE;
-            }
-          } else {
-            con->hpt_data[2]=1;
-          }
-        }
-      } else {
-        // look for RAID-type device
-        int i;
-        char *s;
-        
-        // make a copy of the string to mess with
-        if (!(s = strdup(optarg))) {
-          con->dont_print = FALSE;
-          pout("No memory for argument of -d. Exiting...\n");
-          exit(FAILCMD);
-        } else if (!strncmp(s,"3ware,",6)) {
-            if (split_report_arg2(s, &i)) {
-                 sprintf(extraerror, "Option -d 3ware,N requires N to be a non-negative integer\n");
-                 badarg = TRUE;
-            } else if (i<0 || i>31) {
-                 sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 31\n", i);
-                 badarg = TRUE;
-            } else {
-               // NOTE: controller_port == disk number + 1
-               con->controller_type = CONTROLLER_3WARE;
-                 con->controller_port = i+1;
-            }
-           free(s);
-        } else if (!strncmp(s,"cciss,",6)) {
-             if (split_report_arg2(s, &i)) {
-                 sprintf(extraerror, "Option -d cciss,N requires N to be a non-negative integer\n");
-                 badarg = TRUE;
-             } else if (i<0 || i>127) {
-                 sprintf(extraerror, "Option -d cciss,N (N=%d) must have 0 <= N <= 127\n", i);
-                 badarg = TRUE;
-             } else {
-               // NOTE: controller_port == drive number
-               con->controller_type = CONTROLLER_CCISS;
-               con->controller_port = i+1;
-             }
-             free(s);
-        } else
-           badarg=TRUE;
-      }
+      type = optarg;
       break;
     case 'T':
       if (!strcmp(optarg,"normal")) {
-        con->conservative = FALSE;
+        con->conservative = false;
         con->permissive   = 0;
       } else if (!strcmp(optarg,"conservative")) {
-        con->conservative = TRUE;
+        con->conservative = true;
       } else if (!strcmp(optarg,"permissive")) {
         if (con->permissive<0xff)
           con->permissive++;
       } else if (!strcmp(optarg,"verypermissive")) {
-        con->permissive=0xff;
+        con->permissive = 0xff;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'b':
       if (!strcmp(optarg,"warn")) {
-        con->checksumfail   = FALSE;
-        con->checksumignore = FALSE;
+        checksum_err_mode = CHECKSUM_ERR_WARN;
       } else if (!strcmp(optarg,"exit")) {
-        con->checksumfail   = TRUE;
-        con->checksumignore = FALSE;
+        checksum_err_mode = CHECKSUM_ERR_EXIT;
       } else if (!strcmp(optarg,"ignore")) {
-        con->checksumignore = TRUE;
-        con->checksumfail   = FALSE;
+        checksum_err_mode = CHECKSUM_ERR_IGNORE;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'r':
@@ -538,13 +334,10 @@ void ParseOpts (int argc, char** argv){
         // split_report_arg() may modify its first argument string, so use a
         // copy of optarg in case we want optarg for an error message.
         if (!(s = strdup(optarg))) {
-          con->dont_print = FALSE;
-          pout("Can't allocate memory to copy argument to -r option"
-               " - exiting\n");
-          EXIT(FAILCMD);
+          throw std::bad_alloc();
         }
         if (split_report_arg(s, &i)) {
-          badarg = TRUE;
+          badarg = true;
         } else if (!strcmp(s,"ioctl")) {
           con->reportataioctl  = con->reportscsiioctl = i;
         } else if (!strcmp(s,"ataioctl")) {
@@ -552,132 +345,213 @@ void ParseOpts (int argc, char** argv){
         } else if (!strcmp(s,"scsiioctl")) {
           con->reportscsiioctl = i;
         } else {
-          badarg = TRUE;
+          badarg = true;
         }
         free(s);
       }
       break;
     case 's':
       if (!strcmp(optarg,"on")) {
-        con->smartenable  = TRUE;
-        con->smartdisable = FALSE;
+        ataopts.smart_enable  = scsiopts.smart_enable  = true;
+        ataopts.smart_disable = scsiopts.smart_disable = false;
       } else if (!strcmp(optarg,"off")) {
-        con->smartdisable = TRUE;
-        con->smartenable  = FALSE;
+        ataopts.smart_disable = scsiopts.smart_disable = true;
+        ataopts.smart_enable  = scsiopts.smart_enable  = false;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'o':
       if (!strcmp(optarg,"on")) {
-        con->smartautoofflineenable  = TRUE;
-        con->smartautoofflinedisable = FALSE;
+        ataopts.smart_auto_offl_enable  = true;
+        ataopts.smart_auto_offl_disable = false;
       } else if (!strcmp(optarg,"off")) {
-        con->smartautoofflinedisable = TRUE;
-        con->smartautoofflineenable  = FALSE;
+        ataopts.smart_auto_offl_disable = true;
+        ataopts.smart_auto_offl_enable  = false;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'S':
       if (!strcmp(optarg,"on")) {
-        con->smartautosaveenable  = TRUE;
-        con->smartautosavedisable = FALSE;
+        ataopts.smart_auto_save_enable  = scsiopts.smart_auto_save_enable  = true;
+        ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = false;
       } else if (!strcmp(optarg,"off")) {
-        con->smartautosavedisable = TRUE;
-        con->smartautosaveenable  = FALSE;
+        ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = true;
+        ataopts.smart_auto_save_enable  = scsiopts.smart_auto_save_enable  = false;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'H':
-      con->checksmart = TRUE;           
+      ataopts.smart_check_status = scsiopts.smart_check_status = true;
       break;
     case 'F':
       if (!strcmp(optarg,"none")) {
-        con->fixfirmwarebug = FIX_NONE;
+        ataopts.fix_firmwarebug = FIX_NONE;
       } else if (!strcmp(optarg,"samsung")) {
-        con->fixfirmwarebug = FIX_SAMSUNG;
+        ataopts.fix_firmwarebug = FIX_SAMSUNG;
       } else if (!strcmp(optarg,"samsung2")) {
-        con->fixfirmwarebug = FIX_SAMSUNG2;
+        ataopts.fix_firmwarebug = FIX_SAMSUNG2;
       } else if (!strcmp(optarg,"samsung3")) {
-        con->fixfirmwarebug = FIX_SAMSUNG3;
+        ataopts.fix_firmwarebug = FIX_SAMSUNG3;
       } else if (!strcmp(optarg,"swapid")) {
-        con->fixswappedid = TRUE;
+        ataopts.fix_swapped_id = true;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'c':
-      con->generalsmartvalues = TRUE;
+      ataopts.smart_general_values = true;
       break;
     case 'A':
-      con->smartvendorattrib = TRUE;
+      ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true;
       break;
     case 'l':
       if (!strcmp(optarg,"error")) {
-        con->smarterrorlog = TRUE;
+        ataopts.smart_error_log = scsiopts.smart_error_log = true;
       } else if (!strcmp(optarg,"selftest")) {
-        con->smartselftestlog = TRUE;
+        ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true;
       } else if (!strcmp(optarg, "selective")) {
-        con->selectivetestlog = TRUE;
+        ataopts.smart_selective_selftest_log = true;
       } else if (!strcmp(optarg,"directory")) {
-        con->smartlogdirectory = TRUE;
+        ataopts.smart_logdir = ataopts.gp_logdir = true; // SMART+GPL
+      } else if (!strcmp(optarg,"directory,s")) {
+        ataopts.smart_logdir = true; // SMART
+      } else if (!strcmp(optarg,"directory,g")) {
+        ataopts.gp_logdir = true; // GPL
+      } else if (!strcmp(optarg,"sasphy")) {
+        scsiopts.sasphy = true;
+      } else if (!strcmp(optarg,"sasphy,reset")) {
+        scsiopts.sasphy = scsiopts.sasphy_reset = true;
+      } else if (!strcmp(optarg,"sataphy")) {
+        ataopts.sataphy = true;
+      } else if (!strcmp(optarg,"sataphy,reset")) {
+        ataopts.sataphy = ataopts.sataphy_reset = true;
       } else if (!strcmp(optarg,"background")) {
-        con->smartbackgroundlog = TRUE;
+        scsiopts.smart_background_log = true;
       } else if (!strcmp(optarg,"scttemp")) {
-        con->scttempsts = con->scttemphist = TRUE;
+        ataopts.sct_temp_sts = ataopts.sct_temp_hist = true;
       } else if (!strcmp(optarg,"scttempsts")) {
-        con->scttempsts = TRUE;
+        ataopts.sct_temp_sts = true;
       } else if (!strcmp(optarg,"scttemphist")) {
-        con->scttemphist = TRUE;
+        ataopts.sct_temp_hist = true;
+
+      } else if (!strncmp(optarg, "xerror", sizeof("xerror")-1)) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        unsigned val = 8;
+        sscanf(optarg, "xerror%n,error%n", &n1, &n2);
+        if (!(n1 == len || n2 == len)) {
+          n1 = n2 = -1;
+          sscanf(optarg, "xerror,%u%n,error%n", &val, &n1, &n2);
+        }
+        if ((n1 == len || n2 == len) && val > 0) {
+          ataopts.smart_ext_error_log = val;
+          ataopts.retry_error_log = (n2 == len);
+        }
+        else
+          badarg = true;
+
+      } else if (!strncmp(optarg, "xselftest", sizeof("xselftest")-1)) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        unsigned val = 25;
+        sscanf(optarg, "xselftest%n,selftest%n", &n1, &n2);
+        if (!(n1 == len || n2 == len)) {
+          n1 = n2 = -1;
+          sscanf(optarg, "xselftest,%u%n,selftest%n", &val, &n1, &n2);
+        }
+        if ((n1 == len || n2 == len) && val > 0) {
+          ataopts.smart_ext_selftest_log = val;
+          ataopts.retry_selftest_log = (n2 == len);
+        }
+        else
+          badarg = true;
+
+      } else if (   !strncmp(optarg, "gplog,"   , sizeof("gplog,"   )-1)
+                 || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) {
+        unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0;
+        int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg);
+        sscanf(optarg, "%*[a-z],0x%x%n,%u%n%c%u%n",
+               &logaddr, &n1, &page, &n2, &sign, &nsectors, &n3);
+        if (len > n2 && n3 == -1 && !strcmp(optarg+n2, "-max")) {
+          nsectors = ~0U; sign = '+'; n3 = len;
+        }
+        bool gpl = (optarg[0] == 'g');
+        const char * erropt = (gpl ? "gplog" : "smartlog");
+        if (!(   n1 == len || n2 == len
+              || (n3 == len && (sign == '+' || sign == '-')))) {
+          sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt);
+          badarg = true;
+        }
+        else if (!(    logaddr <= 0xff && page <= (gpl ? 0xffffU : 0x00ffU)
+                   && 0 < nsectors
+                   && (nsectors <= (gpl ? 0xffffU : 0xffU) || nsectors == ~0U)
+                   && (sign != '-' || page <= nsectors)                       )) {
+          sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt);
+          badarg = true;
+        }
+        else {
+          ata_log_request req;
+          req.gpl = gpl; req.logaddr = logaddr; req.page = page;
+          req.nsectors = (sign == '-' ? nsectors-page+1 : nsectors);
+          ataopts.log_requests.push_back(req);
+        }
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'i':
-      con->driveinfo = TRUE;
-      break;            
+      ataopts.drive_info = scsiopts.drive_info = true;
+      break;
     case 'a':
-      con->driveinfo          = TRUE;
-      con->checksmart         = TRUE;
-      con->generalsmartvalues = TRUE;
-      con->smartvendorattrib  = TRUE;
-      con->smarterrorlog      = TRUE;
-      con->smartselftestlog   = TRUE;
-      con->selectivetestlog   = TRUE;
-      /* con->smartbackgroundlog = TRUE; */
+      ataopts.drive_info           = scsiopts.drive_info          = true;
+      ataopts.smart_check_status   = scsiopts.smart_check_status  = true;
+      ataopts.smart_general_values = true;
+      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = true;
+      ataopts.smart_error_log      = scsiopts.smart_error_log     = true;
+      ataopts.smart_selftest_log   = scsiopts.smart_selftest_log  = true;
+      ataopts.smart_selective_selftest_log = true;
+      /* scsiopts.smart_background_log = true; */
+      break;
+    case 'x':
+      ataopts.drive_info           = scsiopts.drive_info          = true;
+      ataopts.smart_check_status   = scsiopts.smart_check_status  = true;
+      ataopts.smart_general_values = true;
+      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = true;
+      ataopts.smart_ext_error_log  = 8;
+      ataopts.retry_error_log      = true;
+      ataopts.smart_ext_selftest_log = 25;
+      ataopts.retry_selftest_log   = true;
+      scsiopts.smart_error_log     = scsiopts.smart_selftest_log    = true;
+      ataopts.smart_selective_selftest_log = true;
+      ataopts.smart_logdir = ataopts.gp_logdir = true;
+      ataopts.sct_temp_sts = ataopts.sct_temp_hist = true;
+      ataopts.sataphy = true;
+      scsiopts.smart_background_log = true;
+      scsiopts.sasphy = true;
       break;
     case 'v':
       // parse vendor-specific definitions of attributes
       if (!strcmp(optarg,"help")) {
-        char *s;
-        con->dont_print=FALSE;
+        con->dont_print = 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:\n\thelp\n%s\n", s);
-        free(s);
+        pout("The valid arguments to -v are:\n\thelp\n%s\n",
+             create_vendor_attribute_arg_list().c_str());
         EXIT(0);
       }
-      charp=con->attributedefs;
-      if (!charp){
-        pout("Fatal internal error in ParseOpts()\n");
-        EXIT(FAILCMD);
-      }
-      if (parse_attribute_def(optarg, &charp))
-        badarg = TRUE;
+      if (parse_attribute_def(optarg, ataopts.attributedefs))
+        badarg = true;
       break;    
     case 'P':
       if (!strcmp(optarg, "use")) {
-        con->ignorepresets = FALSE;
+        ataopts.ignore_presets = false;
       } else if (!strcmp(optarg, "ignore")) {
-        con->ignorepresets = TRUE;
+        ataopts.ignore_presets = true;
       } else if (!strcmp(optarg, "show")) {
-        con->showpresets = TRUE;
+        ataopts.show_presets = true;
       } else if (!strcmp(optarg, "showall")) {
+        if (!no_defaultdb && !read_default_drive_databases())
+          EXIT(FAILCMD);
         if (optind < argc) { // -P showall MODEL [FIRMWARE]
           int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
           EXIT(cnt); // report #matches
@@ -686,28 +560,31 @@ void ParseOpts (int argc, char** argv){
           EXIT(FAILCMD); // report regexp syntax error
         EXIT(0);
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 't':
       if (!strcmp(optarg,"offline")) {
-        con->smartexeoffimmediate = TRUE;
-        con->testcase             = OFFLINE_FULL_SCAN;
+        testcnt++;
+        ataopts.smart_selftest_type = OFFLINE_FULL_SCAN;
+        scsiopts.smart_default_selftest = true;
       } else if (!strcmp(optarg,"short")) {
-        con->smartshortselftest = TRUE;
-        con->testcase           = SHORT_SELF_TEST;
+        testcnt++;
+        ataopts.smart_selftest_type = SHORT_SELF_TEST;
+        scsiopts.smart_short_selftest = true;
       } else if (!strcmp(optarg,"long")) {
-        con->smartextendselftest = TRUE;
-        con->testcase            = EXTEND_SELF_TEST;
+        testcnt++;
+        ataopts.smart_selftest_type = EXTEND_SELF_TEST;
+        scsiopts.smart_extend_selftest = true;
       } else if (!strcmp(optarg,"conveyance")) {
-        con->smartconveyanceselftest = TRUE;
-        con->testcase            = CONVEYANCE_SELF_TEST;
+        testcnt++;
+        ataopts.smart_selftest_type = CONVEYANCE_SELF_TEST;
       } else if (!strcmp(optarg,"afterselect,on")) {
-       // scan remainder of disk after doing selected segments
-       con->scanafterselect=2;
+        // scan remainder of disk after doing selected segment
+        ataopts.smart_selective_args.scan_after_select = 2;
       } else if (!strcmp(optarg,"afterselect,off")) {
-       // don't scan remainder of disk after doing selected segments
-       con->scanafterselect=1;
+        // don't scan remainder of disk after doing selected segments
+        ataopts.smart_selective_args.scan_after_select = 1;
       } else if (!strncmp(optarg,"pending,",strlen("pending,"))) {
        // parse number of minutes that test should be pending
        int i;
@@ -716,21 +593,22 @@ void ParseOpts (int argc, char** argv){
        i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10);
        if (errno || *tailptr != '\0') {
          sprintf(extraerror, "Option -t pending,N requires N to be a non-negative integer\n");
-         badarg = TRUE;
+          badarg = true;
        } else if (i<0 || i>65535) {
          sprintf(extraerror, "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i);
-         badarg = TRUE;
+          badarg = true;
        } else {
-         con->pendingtime=i+1;
+          ataopts.smart_selective_args.pending_time = i+1;
        }
       } else if (!strncmp(optarg,"select",strlen("select"))) {
+        testcnt++;
         // parse range of LBAs to test
         uint64_t start, stop; int mode;
         if (split_selective_arg(optarg, &start, &stop, &mode)) {
          sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n");
-          badarg = TRUE;
+          badarg = true;
         } else {
-          if (con->smartselectivenumspans >= 5 || start > stop) {
+          if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) {
             if (start > stop) {
               sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n",
                 start, stop, optarg);
@@ -738,58 +616,69 @@ void ParseOpts (int argc, char** argv){
               sprintf(extraerror,"ERROR: No more than five selective self-test spans may be"
                 " defined\n");
             }
-           badarg = TRUE;
+            badarg = true;
           }
-          con->smartselectivespan[con->smartselectivenumspans][0] = start;
-          con->smartselectivespan[con->smartselectivenumspans][1] = stop;
-          con->smartselectivemode[con->smartselectivenumspans] = mode;
-          con->smartselectivenumspans++;
-          con->testcase            = SELECTIVE_SELF_TEST;
+          ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].start = start;
+          ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].end   = stop;
+          ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].mode  = mode;
+          ataopts.smart_selective_args.num_spans++;
+          ataopts.smart_selftest_type = SELECTIVE_SELF_TEST;
         }
       } else if (!strncmp(optarg, "scttempint,", sizeof("scstempint,")-1)) {
         unsigned interval = 0; int n1 = -1, n2 = -1, len = strlen(optarg);
         if (!(   sscanf(optarg,"scttempint,%u%n,p%n", &interval, &n1, &n2) == 1
               && 0 < interval && interval <= 0xffff && (n1 == len || n2 == len))) {
             strcpy(extraerror, "Option -t scttempint,N[,p] must have positive integer N\n");
-            badarg = TRUE;
+            badarg = true;
         }
-        con->scttempint = interval;
-        con->scttempintp = (n2 == len);
+        ataopts.sct_temp_int = interval;
+        ataopts.sct_temp_int_pers = (n2 == len);
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'C':
-      captive = TRUE;
+      captive = true;
       break;
     case 'X':
-      con->smartselftestabort = TRUE;
-      con->testcase           = ABORT_SELF_TEST;
+      testcnt++;
+      scsiopts.smart_selftest_abort = true;
+      ataopts.smart_selftest_type = ABORT_SELF_TEST;
       break;
     case 'n':
       // skip disk check if in low-power mode
       if (!strcmp(optarg, "never"))
-        con->powermode = 1; // do not skip, but print mode
+        ataopts.powermode = 1; // do not skip, but print mode
       else if (!strcmp(optarg, "sleep"))
-        con->powermode = 2;
+        ataopts.powermode = 2;
       else if (!strcmp(optarg, "standby"))
-        con->powermode = 3;
+        ataopts.powermode = 3;
       else if (!strcmp(optarg, "idle"))
-        con->powermode = 4;
+        ataopts.powermode = 4;
       else
-        badarg = TRUE;
+        badarg = true;
+      break;
+    case 'B':
+      {
+        const char * path = optarg;
+        if (*path == '+' && path[1])
+          path++;
+        else
+          no_defaultdb = true;
+        if (!read_drive_database(path))
+          EXIT(FAILCMD);
+      }
       break;
     case 'h':
-      con->dont_print=FALSE;
+      con->dont_print = false;
       printslogan();
       Usage();
       EXIT(0);  
       break;
     case '?':
     default:
-      con->dont_print=FALSE;
+      con->dont_print = 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.
@@ -805,7 +694,6 @@ void ParseOpts (int argc, char** argv){
         UsageSummary();
         EXIT(FAILCMD);
       }
-#endif
       if (optopt) {
         // Iff optopt holds a valid option then argument must be
         // missing.  Note (BA) this logic seems to fail using Solaris
@@ -842,12 +730,11 @@ void ParseOpts (int argc, char** argv){
   // print output is switchable, then start with the print output
   // turned off
   if (con->printing_switchable)
-    con->dont_print=TRUE;
+    con->dont_print = false;
 
   // 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->smartselectivenumspans>0?1:0))){
-    con->dont_print=FALSE;
+  if (testcnt > 1) {
+    con->dont_print = false;
     printslogan();
     pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n");
     UsageSummary();
@@ -856,10 +743,11 @@ void ParseOpts (int argc, char** argv){
 
   // error message if user has set selective self-test options without
   // asking for a selective self-test
-  if ((con->pendingtime || con->scanafterselect) && !con->smartselectivenumspans){
-    con->dont_print=FALSE;
+  if (   (ataopts.smart_selective_args.pending_time || ataopts.smart_selective_args.scan_after_select)
+      && !ataopts.smart_selective_args.num_spans) {
+    con->dont_print = false;
     printslogan();
-    if (con->pendingtime)
+    if (ataopts.smart_selective_args.pending_time)
       pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n");
     else
       pout("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n");
@@ -868,26 +756,26 @@ void ParseOpts (int argc, char** argv){
   }
 
   // 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;
-  }
-  else if (captive && con->smartconveyanceselftest) {
-    con->smartconveyanceselftest    = FALSE;
-    con->smartconveyancecapselftest = TRUE;
-    con->testcase                   = CONVEYANCE_CAPTIVE_SELF_TEST;
-  }
-  else if (captive && con->smartselectiveselftest) {
-    con->smartselectiveselftest    = FALSE;
-    con->smartselectivecapselftest = TRUE;
-    con->testcase                  = SELECTIVE_CAPTIVE_SELF_TEST;
-  }
+  if (captive)
+    switch (ataopts.smart_selftest_type) {
+      case SHORT_SELF_TEST:
+        ataopts.smart_selftest_type = SHORT_CAPTIVE_SELF_TEST;
+        scsiopts.smart_short_selftest     = false;
+        scsiopts.smart_short_cap_selftest = true;
+        break;
+      case EXTEND_SELF_TEST:
+        ataopts.smart_selftest_type = EXTEND_CAPTIVE_SELF_TEST;
+        scsiopts.smart_extend_selftest     = false;
+        scsiopts.smart_extend_cap_selftest = true;
+        break;
+      case CONVEYANCE_SELF_TEST:
+        ataopts.smart_selftest_type = CONVEYANCE_CAPTIVE_SELF_TEST;
+        break;
+      case SELECTIVE_SELF_TEST:
+        ataopts.smart_selftest_type = SELECTIVE_CAPTIVE_SELF_TEST;
+        break;
+    }
+
   // From here on, normal operations...
   printslogan();
   
@@ -907,7 +795,13 @@ void ParseOpts (int argc, char** argv){
       pout("%s\n",argv[optind+i]);
     UsageSummary();
     EXIT(FAILCMD);
-  }  
+  }
+
+  // Read or init drive database
+  if (!no_defaultdb && !read_default_drive_databases())
+    EXIT(FAILCMD);
+
+  return type;
 }
 
 // Printing function (controlled by global con->dont_print) 
@@ -949,113 +843,152 @@ void PrintOut(int priority, const char *fmt, ...) {
   return;
 }
 
+// Used to warn users about invalid checksums. Called from atacmds.cpp.
+// Action to be taken may be altered by the user.
+void checksumwarning(const char * string)
+{
+  // user has asked us to ignore checksum errors
+  if (checksum_err_mode == CHECKSUM_ERR_IGNORE)
+    return;
 
-/* Main Program */
-int main (int argc, char **argv){
-  int fd,retval=0;
-  char *device;
-  smartmonctrl control;
-  char *mode=NULL;
+  pout("Warning! %s error: invalid SMART checksum.\n", string);
 
-  // define control block for external functions
-  con=&control;
+  // user has asked us to fail on checksum errors
+  if (checksum_err_mode == CHECKSUM_ERR_EXIT)
+    EXIT(FAILSMART);
+}
 
-  // Part input arguments
-  ParseOpts(argc,argv);
+// Return info string about device protocol
+static const char * get_protocol_info(const smart_device * dev)
+{
+  switch ((int)dev->is_ata() | ((int)dev->is_scsi() << 1)) {
+    case 0x1: return "ATA";
+    case 0x2: return "SCSI";
+    case 0x3: return "ATA+SCSI";
+    default:  return "Unknown";
+  }
+}
 
-  device = argv[argc-1];
+// Main program without exception handling
+int main_worker(int argc, char **argv)
+{
+  // Initialize interface
+  smart_interface::init();
+  if (!smi())
+    return 1;
+
+  int retval = 0;
+
+  smart_device * dev = 0;
+  try {
+    // define control block for external functions
+    smartmonctrl control;
+    con=&control;
+
+    // Parse input arguments
+    ata_print_options ataopts;
+    scsi_print_options scsiopts;
+    const char * type = parse_options(argc, argv, ataopts, scsiopts);
+
+    // '-d test' -> Report result of autodetection
+    bool print_type_only = (type && !strcmp(type, "test"));
+    if (print_type_only)
+      type = 0;
+
+    const char * name = argv[argc-1];
+
+    if (!strcmp(name,"-")) {
+      // Parse "smartctl -r ataioctl,2 ..." output from stdin
+      if (type || print_type_only) {
+        pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+        UsageSummary();
+        return FAILCMD;
+      }
+      dev = get_parsed_ata_device(smi(), name);
+    }
+    else
+      // get device of appropriate type
+      dev = smi()->get_smart_device(name, type);
 
-  // Device name "-": Parse "smartctl -r ataioctl,2 ..." output
-  if (!strcmp(device,"-")) {
-    if (con->controller_type != CONTROLLER_UNKNOWN) {
-      pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+    if (!dev) {
+      pout("%s: %s\n", name, smi()->get_errmsg());
+      if (type)
+        printvalidarglistmessage('d');
+      else
+        pout("Smartctl: please specify device type with the -d option.\n");
       UsageSummary();
       return FAILCMD;
     }
-    con->controller_type = CONTROLLER_PARSEDEV;
-  }
 
-  // If use has specified 3ware controller, determine which interface 
-  if (con->controller_type == CONTROLLER_3WARE) {
-    con->controller_type=guess_device_type(device);
-    if (con->controller_type!=CONTROLLER_3WARE_9000_CHAR && con->controller_type!=CONTROLLER_3WARE_678K_CHAR)
-      con->controller_type = CONTROLLER_3WARE_678K;
+    if (print_type_only)
+      // Report result of first autodetection
+      pout("%s: Device of type '%s' [%s] detected\n",
+           dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev));
+
+    // Open device
+    {
+      // Save old info
+      smart_device::device_info oldinfo = dev->get_info();
+
+      // Open with autodetect support, may return 'better' device
+      dev = dev->autodetect_open();
+
+      // Report if type has changed
+      if ((type || print_type_only) && oldinfo.dev_type != dev->get_dev_type())
+        pout("%s: Device open changed type from '%s' to '%s'\n",
+          dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type());
+    }
+    if (!dev->is_open()) {
+      pout("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg());
+      delete dev;
+      return FAILDEV;
+    }
+
+    // now call appropriate ATA or SCSI routine
+    if (print_type_only)
+      pout("%s: Device of type '%s' [%s] opened\n",
+           dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev));
+    else if (dev->is_ata())
+      retval = ataPrintMain(dev->to_ata(), ataopts);
+    else if (dev->is_scsi())
+      retval = scsiPrintMain(dev->to_scsi(), scsiopts);
+    else
+      // we should never fall into this branch!
+      pout("%s: Neither ATA nor SCSI device\n", dev->get_info_name());
+
+    dev->close();
+    delete dev;
+  }
+  catch (...) {
+    delete dev;
+    throw;
   }
+  return retval;
+}
 
-  if (con->controller_type == CONTROLLER_UNKNOWN)
-    con->controller_type=guess_device_type(device);
-  
-  if (con->controller_type == CONTROLLER_UNKNOWN) {
-    pout("Smartctl: please specify device type with the -d option.\n");
-    UsageSummary();
-    return FAILCMD;
+
+// Main program
+int main(int argc, char **argv)
+{
+  int status;
+  try {
+    // Do the real work ...
+    status = main_worker(argc, argv);
   }
-  
-  // set up mode for open() call.  SCSI case is:
-  switch (con->controller_type) {
-  case CONTROLLER_SCSI:
-  case CONTROLLER_SAT:
-    mode="SCSI";
-    break;
-  case CONTROLLER_3WARE_9000_CHAR:
-    mode="ATA_3WARE_9000";
-    break;
-  case CONTROLLER_3WARE_678K_CHAR:
-    mode="ATA_3WARE_678K";
-    break;
-  case CONTROLLER_CCISS:
-    mode="CCISS";
-    break;
-  default:
-    mode="ATA";
-    break;
+  catch (int ex) {
+    // EXIT(status) arrives here
+    status = ex;
   }
-  
-  // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the
-  // scsi generic device can be used (needs write permission for MODE 
-  // SELECT command) plus O_NONBLOCK to stop open hanging if media not
-  // present (e.g. with st).  Opening is retried O_RDONLY if read-only
-  // media prevents opening O_RDWR (it cannot happen for scsi generic
-  // devices, but it can for the others).
-  if (con->controller_type != CONTROLLER_PARSEDEV)
-    fd = deviceopen(device, mode);
-  else
-    fd = parsedev_open(device);
-  if (fd<0) {
-    char errmsg[256];
-    snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
-    errmsg[255]='\0';
-    syserror(errmsg);
-    return FAILDEV;
+  catch (const std::bad_alloc & /*ex*/) {
+    // Memory allocation failed (also thrown by std::operator new)
+    pout("Smartctl: Out of memory\n");
+    status = FAILCMD;
   }
-
-  // now call appropriate ATA or SCSI routine
-  switch (con->controller_type) {
-  case CONTROLLER_UNKNOWN:
-    // we should never fall into this branch!
-    pout("Smartctl: please specify device type with the -d option.\n");
-    UsageSummary();
-    retval = FAILCMD;
-    break;
-  case CONTROLLER_SCSI:
-    retval = scsiPrintMain(fd);
-    if ((0 == retval) && (CONTROLLER_SAT == con->controller_type))
-        retval = ataPrintMain(fd);
-    break;
-  case CONTROLLER_CCISS:
-    // route the cciss command through scsiPrintMain. 
-    // cciss pass-throughs will separeate from the SCSI data-path.
-    retval = scsiPrintMain(fd);
-    break;
-  default:
-    retval = ataPrintMain(fd);
-    break;
+  catch (const std::exception & ex) {
+    // Other fatal errors
+    pout("Smartctl: Exception: %s\n", ex.what());
+    status = FAILCMD;
   }
-  
-  if (con->controller_type != CONTROLLER_PARSEDEV)
-    deviceclose(fd);
-  else
-    parsedev_close(fd);
-
-  return retval;
+  return status;
 }
+