diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index d8085b55d626f22bf3a65a2a1aeb0dfd1055557b..dcf7bf2e6c48d86de9d7d6b1968a24a07e6258f6 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.19 2002/10/24 11:38:11 ballen4705 Exp $
+$Id: CHANGELOG,v 1.20 2002/10/25 14:15:05 ballen4705 Exp $
 
 Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
@@ -28,8 +28,15 @@ NOTES FOR NEXT RELEASE:
 
 smartmontools-5.0-12
 
+    smartd on startup now looks in the configuration file /etc/smartd.conf for
+    a list of devices which to include in its monitoring list.  See man page
+    (man smartd) for syntax.
+
     smartd: close file descriptors of SCSI device if not SMART capable
+    Closes ALL file descriptors after forking to daemon.
+
     added new temperature attribute (231, temperature)
+
     smartd: now open ATA disks using O_RDONLY
 
 smartmontools-5.0-11
diff --git a/sm5/smartd.8 b/sm5/smartd.8
index c8747ab21f8c1434e2439371e8bcf6615a24729f..8bdea6065e5f57659a0faf333ac2c1ab8743543b 100644
--- a/sm5/smartd.8
+++ b/sm5/smartd.8
@@ -13,7 +13,7 @@
 \# 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/
-.TH SMARTD 8  "$Date: 2002/10/24 10:56:10 $" "smartmontools-5.0"
+.TH SMARTD 8  "$Date: 2002/10/25 14:15:05 $" "smartmontools-5.0"
 .SH NAME
 smartd \- S.M.A.R.T. Daemon
 .SH SYNOPSIS
@@ -33,7 +33,11 @@ REFERENCES below)
 .B smartd  
 will notify users of S.M.A.R.T. errors and changes of
 S.M.A.R.T. attributes via the SYSLOG interface.  These notifications
-and warnings normally appear in /var/log/messages.
+and warnings normally appear in 
+.B /var/log/messages.
+It can be configured at start-up
+using the file
+.B /etc/smartd.conf.
 
 .PP
 .SH SYNTAX
@@ -44,7 +48,9 @@ takes either no arguments, or a single argument.  The optional
 argument begins with a '\-' followed by a letter or letters. Multiple
 options must begin with a single '\-'.
 
-The 
+In the absense of the configuration file
+.B /etc/smartd.conf
+the 
 .B
 smartd
 daemon scans for all devices that support S.M.A.R.T., using
@@ -52,10 +58,11 @@ daemon scans for all devices that support S.M.A.R.T., using
 polls these devices every 30 minutes checking for S.M.A.R.T. errors.
 [Note that on start-up, when
 .B
-smartd
-scans for devices, a warning message may appear in
-/var/log/messages, about missing block-major-xx devices.  These
-messages are harmless.]
+smartd scans for devices, a warning message may appear in
+.B /var/log/messages,
+about missing block-major-xx devices.  These
+messages are usually harmless. Alternatively, the configuration file can be
+used to list devices to search for at start up.]
 .P
 .SH 
 OPTIONS
@@ -103,6 +110,36 @@ and disabled using the command:
 .nf
 .B '/sbin/chkconfig --del smartd'
 
+.SH CONFIGURATION FILE
+In the absence of a configuration file,
+.B smartd 
+will try to open the 12 ATA devices /dev/hd[a-l] and the 26
+SCSI devices /dev/sd[a-z].  This can be annoying if you have an ATA or
+SCSI device that hangs or misbehaves when receiving SMART commands.
+Even if this causes no problems, you may be annoyed by the string of
+error log messages about block-major devices that can't be found, and
+SCSI devices that can't be opened.
+
+A simple solution is to create a configuration file
+.B /etc/smartd.conf
+which should contain a list of devices to monitor, with one device per line.
+For security, this file should not be writable by anyone but root.
+Spaces and tabs in the file are ignored, as are lines starting with a hash sign (#).
+Here is a sample configuration file:
+
+.nf
+.B # This is an example smartd startup config file
+.B # /etc/smartd.conf
+.B # for monitoring two IDE disks and one SCSI disk
+
+.B # first IDE disk on each of two interfaces
+.B /dev/hda
+.B /dev/hdc
+
+.B # SCSI disk
+.B /dev/sda
+.fi
+
 .SH NOTES
 .B smartd
 will make log entries if SMART attribute values have changed,
@@ -198,4 +235,4 @@ Please let us know if there is an on\-line source for this document.
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartd.8,v 1.10 2002/10/24 10:56:10 ballen4705 Exp $
+$Id: smartd.8,v 1.11 2002/10/25 14:15:05 ballen4705 Exp $
diff --git a/sm5/smartd.c b/sm5/smartd.c
index f49c781823458c24025ae44a6026e336734677ca..5763c86c33c0331602ec4593390097d1f6d88913 100644
--- a/sm5/smartd.c
+++ b/sm5/smartd.c
@@ -20,6 +20,8 @@
  *
  */
 
+#include <errno.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
@@ -37,7 +39,7 @@
 #include "ataprint.h"
 
 extern const char *CVSid1, *CVSid2;
-const char *CVSid3="$Id: smartd.c,v 1.29 2002/10/24 17:15:25 ballen4705 Exp $" 
+const char *CVSid3="$Id: smartd.c,v 1.30 2002/10/25 14:15:05 ballen4705 Exp $" 
 CVSID1 CVSID4 CVSID7;
 
 int daemon_init(void){
@@ -75,8 +77,8 @@ int daemon_init(void){
 void printout(int priority,char *fmt, ...){
   va_list ap;
   // initialize variable argument list 
- va_start(ap,fmt);
-  if (debugmode)
+  va_start(ap,fmt);
+  if (debugmode) 
     vprintf(fmt,ap);
   else
     vsyslog(priority,fmt,ap);
@@ -109,119 +111,130 @@ void printhead(){
 
 /* prints help information for command syntax */
 void Usage (void){
-  printhead();
   printout(LOG_INFO,"usage: smartd -[opts] \n\n");
   printout(LOG_INFO,"Read Only Options:\n");
   printout(LOG_INFO,"   %c  Start smartd in debug Mode\n",DEBUGMODE);
-  printout(LOG_INFO,"   %c  Print License, Copyright, and version information\n",PRINTCOPYLEFT);
+  printout(LOG_INFO,"   %c  Print License, Copyright, and version information\n\n",PRINTCOPYLEFT);
+  printout(LOG_INFO,"Configuration file: /etc/smartd.conf\n");
 }
 	
 // scan to see what ata devices there are, and if they support SMART
-void atadevicescan ( atadevices_t *devices){
-  int i;
+int atadevicescan (atadevices_t *devices, char *device){
   int fd;
   struct hd_driveid drive;
-  char device[] = "/dev/hda";
   
-  for(i=0;i<MAXATADEVICES;i++,device[7]++ ){
-    
-    printout(LOG_INFO,"Reading Device %s\n", device);
-    
-    fd = open(device, O_RDONLY);
-    if (fd < 0)
-      // no such device
-      continue;
-    
-    if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
-      // device exists, but not able to do SMART
-      close(fd);
-      printout(LOG_INFO,"Device: %s, Found but not SMART capable, or couldn't enable SMART\n",device);
-      continue;
-    }
-
-    // Does device support read values and read thresholds?  We should
-    // modify this next block for devices that do support SMART status
-    // but don't support read values and read thresholds.
-    if (ataReadSmartValues (fd,&devices[numatadevices].smartval)){
-      close(fd);
-      printout(LOG_INFO,"Device: %s, Read SMART Values Failed\n",device);
-      continue;
-    }
-    else if (ataReadSmartThresholds (fd,&devices[numatadevices].smartthres)){
-      close(fd);
-      printout(LOG_INFO,"Device: %s, Read SMART Thresholds Failed\n",device);
-      continue;
-    }
-
-    // device exists, and does SMART.  Add to list
-    printout(LOG_INFO,"%s Found and is SMART capable\n",device);
-    devices[numatadevices].fd = fd;
-    strcpy(devices[numatadevices].devicename, device);
-    devices[numatadevices].drive = drive;
-        
-    // This makes NO sense.  We may want to know if the drive supports
-    // Offline Surface Scan, for example.  But checking if it supports
-    // self-tests seems useless. In any case, smartd NEVER uses this
-    // field anywhere...
-    devices[numatadevices].selftest = 
-      isSupportSelfTest(devices[numatadevices].smartval);
-
-    numatadevices++;
+  printout(LOG_INFO,"Opening device %s\n", device);
+  fd = open(device, O_RDONLY);
+  if (fd < 0) {
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
+    else
+      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+    return 1;
+  }
+  
+  if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
+    // device exists, but not able to do SMART
+    close(fd);
+    printout(LOG_INFO,"Device: %s, Found but not SMART capable, or couldn't enable SMART\n",device);
+    return 2;
+  }
+  
+  // Does device support read values and read thresholds?  We should
+  // modify this next block for devices that do support SMART status
+  // but don't support read values and read thresholds.
+  if (ataReadSmartValues (fd,&devices[numatadevices].smartval)){
+    close(fd);
+    printout(LOG_INFO,"Device: %s, Read SMART Values Failed\n",device);
+    return 3;
   }
+  else if (ataReadSmartThresholds (fd,&devices[numatadevices].smartthres)){
+    close(fd);
+    printout(LOG_INFO,"Device: %s, Read SMART Thresholds Failed\n",device);
+    return 4;
+  }
+  
+  // device exists, and does SMART.  Add to list
+  printout(LOG_INFO,"%s Found and is SMART capable. Adding to \"monitor\" list.\n",device);
+  devices[numatadevices].fd = fd;
+  strcpy(devices[numatadevices].devicename, device);
+  devices[numatadevices].drive = drive;
+  
+  // This makes NO sense.  We may want to know if the drive supports
+  // Offline Surface Scan, for example.  But checking if it supports
+  // self-tests seems useless. In any case, smartd NEVER uses this
+  // field anywhere...
+  devices[numatadevices].selftest = 
+    isSupportSelfTest(devices[numatadevices].smartval);
+  
+  numatadevices++;
+  return 0;
 }
 
-
-
 // This function is hard to read and ought to be rewritten. Why in the
 // world is the four-byte integer cast to a pointer to an eight-byte
 // object??
-void scsidevicescan ( scsidevices_t *devices){
+int scsidevicescan (scsidevices_t *devices, char *device){
   int i, fd, smartsupport;
   unsigned char  tBuf[4096];
-  char device[] = "/dev/sda";	
   
-  for(i = 0; i < MAXSCSIDEVICES ; i++,device[7]++ ){
-    
-    printout(LOG_INFO,"Reading Device %s\n", device);
-    
-    fd=open (device, O_RDWR);
-    
-    if (fd<0)
-      continue;
+  
+  // open device
+  printout(LOG_INFO,"Opening device %s\n", device);
+  fd=open(device, O_RDWR);
+  if (fd<0) {
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
+    else
+      printout(LOG_INFO,"Device: %s, Opening device failed\n", device);
+    return 1;
+  }
+
+  // check that it's ready for commands
+  if (!testunitready(fd)){
+    printout(LOG_INFO,"Device: %s, Failed Test Unit Ready\n", device);
+    close(fd);
+    return 2;
+  }
+  
+  // make sure that we can read mode page
+  if (modesense(fd, 0x1c, (UINT8 *) &tBuf)){
+    printout(LOG_INFO,"Device: %s, Failed read of ModePage 0x1c\n", device);
+    close(fd);
+    return 3;
+  }
+  
+  // see if SMART is supported and enabled
+  if (scsiSmartSupport(fd, (UINT8 *) &smartsupport) ||
+      (smartsupport & DEXCPT_ENABLE)){
+    printout(LOG_INFO,"Device: %s, SMART not supported or not enabled\n", device);
+    close(fd);
+    return 4;
+  }
 
-    if (!testunitready (fd)) {
-      if (modesense(fd, 0x1c, (UINT8 *) &tBuf)){
-	printout(LOG_INFO,"Device: %s, Failed read of ModePage 1C \n", device);
-	close(fd);
+  // now we can proceed to register the device
+  printout(LOG_INFO, "Device: %s, Found and is SMART capable. Adding to \"monitor\" list.\n",device);
+  devices[numscsidevices].fd = fd;
+  strcpy(devices[numscsidevices].devicename,device);
+
+  // register the supported functionality.  The smartd code does not
+  // seem to make any further use of this information.
+  if (logsense(fd, SUPPORT_LOG_PAGES, (UINT8 *) &tBuf) == 0){
+    for ( i = 4; i < tBuf[3] + LOGPAGEHDRSIZE ; i++){
+      switch ( tBuf[i]){ 
+      case TEMPERATURE_PAGE:
+	devices[numscsidevices].TempPageSupported = 1;
+	break;
+      case SMART_PAGE:
+	devices[numscsidevices].SmartPageSupported = 1;
+	break;
+      default:
+	break;
       }
-      else
-	if (!scsiSmartSupport( fd, (UINT8 *) &smartsupport) &&
-	    !(smartsupport & DEXCPT_ENABLE)){
-	  devices[numscsidevices].fd = fd;
-	  strcpy(devices[numscsidevices].devicename,device);
-	  
-	  printout(LOG_INFO, "Device: %s, Found and is SMART capable\n",device);
-	  
-	  if (logsense ( fd , SUPPORT_LOG_PAGES, (UINT8 *) &tBuf) == 0){
-	    for ( i = 4; i < tBuf[3] + LOGPAGEHDRSIZE ; i++){
-	      switch ( tBuf[i]){ 
-	      case TEMPERATURE_PAGE:
-		devices[numscsidevices].TempPageSupported = 1;
-		break;
-	      case SMART_PAGE:
-		devices[numscsidevices].SmartPageSupported = 1;
-		break;
-	      default:
-		break;
-	      }
-	    }	
-	  }
-	  numscsidevices++;
-	}
-	else
-	  close(fd);
-    }
+    }	
   }
+  numscsidevices++;
+  return 0;
 }
 
 
@@ -295,8 +308,8 @@ int ataCheckDevice( atadevices_t *drive){
 }
 
 
-int scsiCheckDevice( scsidevices_t *drive)
-{
+
+int scsiCheckDevice( scsidevices_t *drive){
   UINT8 returnvalue;
   UINT8 currenttemp;
   UINT8 triptemp;
@@ -312,6 +325,8 @@ int scsiCheckDevice( scsidevices_t *drive)
   else
     printout(LOG_INFO,"Device: %s, Acceptable attribute: %d\n", drive->devicename, returnvalue);  
   
+  // Seems to completely ignore what capabilities were found on the
+  // device when scanned
   if (currenttemp){
     if ( (currenttemp != drive->Temperature) && ( drive->Temperature) )
       printout(LOG_INFO, "Device: %s, Temperature changed %d degrees to %d degrees since last reading\n", 
@@ -414,23 +429,111 @@ char copyleftstring[]=
 "under the terms of the GNU General Public License Version 2.\n"
 "See http://www.gnu.org for further details.\n\n";
 
-const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'\0' };
+cfgfile config[MAXENTRIES];
+
+
+// returns number of entries in config file, or 0 if no config file exists
+int parseconfigfile(){
+  FILE *fp;
+  int entry=0,lineno=0;
+  char line[MAXLINELEN+2];
+  
+  // Open config file, if it exists
+  fp=fopen(CONFIGFILE,"r");
+  if (fp==NULL && errno!=ENOENT){
+    // file exists but we can't read it
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Unable to open configuration file %s\n",
+	       sys_errlist[errno],CONFIGFILE);
+    else
+      printout(LOG_INFO,"Unable to open configuration file %s\n",CONFIGFILE);
+    exit(1);
+  }
+  
+  // No config file
+  if (fp==NULL)
+    return 0;
+  
+  // configuration file exists.  Read it and search for devices
+  printout(LOG_INFO,"Using configuration file %s\n",CONFIGFILE);
+  while (fgets(line,MAXLINELEN+2,fp)){
+    int len;
+    char *dev;
+    
+    // track linenumber for error messages
+    lineno++;
+    
+    // See if line is too long
+    len=strlen(line);
+    if (len>MAXLINELEN){
+      printout(LOG_INFO,"Error: line %d of file %s is more than than %d characters long.\n",
+	       lineno,CONFIGFILE,MAXLINELEN);
+      exit(1); 
+    }
+    
+    // eliminate any terminating newline
+    if (line[len-1]=='\n'){
+      len--;
+      line[len]='\0';
+    }
+    
+    // Skip white space
+    dev=line;
+    while (*dev && (*dev==' ' || *dev=='\t'))
+      dev++;
+    len=strlen(dev);
+    
+    // If line is blank, or a comment, skip it
+    if (!len || *dev=='#')
+      continue;
+    
+    // We've got a legit entry
+    if (entry>=MAXENTRIES){
+      printout(LOG_INFO,"Error: configuration file %s can have no more than %d entries\n",
+	       CONFIGFILE,MAXENTRIES);
+      exit(1);
+    }
+    
+    // Copy information into data structure for after forking
+    strcpy(config[entry].name,dev);
+    config[entry].lineno=lineno;
+    config[entry].tryscsi=config[entry].tryata=1;
+    
+    // Try and recognize if a IDE or SCSI device
+    if (len>5 && !strncmp("/dev/h",dev, 6))
+      config[entry].tryscsi=0;
+    
+    if (len>5 && !strncmp("/dev/s",dev, 6))
+      config[entry].tryata=0;
+    
+    entry++;
+  }
+  fclose(fp);
+  if (entry)
+    return entry;
+  
+  printout(LOG_INFO,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
+  exit(1);
+}
+
+const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'h','?','\0' };
 
 /* Main Program */
 int main (int argc, char **argv){
-  
   atadevices_t atadevices[MAXATADEVICES], *atadevicesptr;
   scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr;
-  int optchar;
+  int optchar,i;
   extern char *optarg;
   extern int  optopt, optind, opterr;
+  int entries;
   
   numatadevices=0;
   numscsidevices=0;
   scsidevicesptr = scsidevices;
   atadevicesptr = atadevices;
-  opterr=1;
+  opterr=optopt=0;
 
+  // Parse input options:
   while (-1 != (optchar = getopt(argc, argv, opts))){
     switch(optchar) {
     case PRINTCOPYLEFT:
@@ -443,14 +546,22 @@ int main (int argc, char **argv){
       emailnotification = TRUE;
       break;
     case '?':
+    case 'h':
     default:
       debugmode=1;
-      printout(LOG_INFO,"\n");
+      if (optopt) {
+	printhead();
+	printout(LOG_INFO,"=======> UNRECOGNIZED OPTION: %c <======= \n\n",optopt);
+	Usage();
+	exit(-1);
+      }
+      printhead();
       Usage();
-      exit(-1);	
+      exit(0);
     }
   }
   
+  // If needed print copyright, license and version information
   if (printcopyleft){
     debugmode=1;
     printhead();
@@ -462,20 +573,39 @@ int main (int argc, char **argv){
     exit(0);
   }
   
+  // print header
   printhead();
  
+  // look in configuration file CONFIGFILE (normally /etc/smartd.conf)
+  entries=parseconfigfile();
 
-   // If we are running in background as a daemon, call 
-  // a routines that forks then closes file descriptors. 
+  // If in background as a daemon, fork and close file descriptors
   if (!debugmode){
     daemon_init();
   }
- 
-  // routines that look for devices on ata and scsi bus 
-  atadevicescan (atadevicesptr); 
-  scsidevicescan (scsidevicesptr);
   
-  CheckDevices ( atadevicesptr, scsidevicesptr); 
+  // If we found a config file, look at its entries
+  if (entries)
+    for (i=0;i<entries;i++){
+      if (config[i].tryata && atadevicescan(atadevicesptr, config[i].name))
+	printout(LOG_INFO,"Unable to register ATA device %s at line %d of file %s\n",
+		 config[i].name, config[i].lineno, CONFIGFILE);
+      
+      if (config[i].tryscsi && scsidevicescan(scsidevicesptr, config[i].name))
+	printout(LOG_INFO,"Unable to register SCSI device %s at line %d of file %s\n",
+		 config[i].name, config[i].lineno, CONFIGFILE);
+    }
+  else {
+    char deviceata[] = "/dev/hda";
+    char devicescsi[]= "/dev/sda";
+    printout(LOG_INFO,"No configuration file %s found. Searching for devices.\n",CONFIGFILE);
+    for(i=0;i<MAXATADEVICES;i++,deviceata[7]++)
+      atadevicescan(atadevicesptr, deviceata);    
+    for(i=0;i<MAXSCSIDEVICES;i++,devicescsi[7]++)
+      scsidevicescan(scsidevicesptr, devicescsi);
+  }
+  
+  CheckDevices(atadevicesptr, scsidevicesptr); 
   return 0;
 }
 
diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp
index 6689dfd42f6d3ca4d9ddd013eb32ee56fe601413..b0e1c3114cc08e128b390563af9e7511f64cc74a 100644
--- a/sm5/smartd.cpp
+++ b/sm5/smartd.cpp
@@ -20,6 +20,8 @@
  *
  */
 
+#include <errno.h>
+#include <stdlib.h>
 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
@@ -37,7 +39,7 @@
 #include "ataprint.h"
 
 extern const char *CVSid1, *CVSid2;
-const char *CVSid3="$Id: smartd.cpp,v 1.29 2002/10/24 17:15:25 ballen4705 Exp $" 
+const char *CVSid3="$Id: smartd.cpp,v 1.30 2002/10/25 14:15:05 ballen4705 Exp $" 
 CVSID1 CVSID4 CVSID7;
 
 int daemon_init(void){
@@ -75,8 +77,8 @@ int daemon_init(void){
 void printout(int priority,char *fmt, ...){
   va_list ap;
   // initialize variable argument list 
- va_start(ap,fmt);
-  if (debugmode)
+  va_start(ap,fmt);
+  if (debugmode) 
     vprintf(fmt,ap);
   else
     vsyslog(priority,fmt,ap);
@@ -109,119 +111,130 @@ void printhead(){
 
 /* prints help information for command syntax */
 void Usage (void){
-  printhead();
   printout(LOG_INFO,"usage: smartd -[opts] \n\n");
   printout(LOG_INFO,"Read Only Options:\n");
   printout(LOG_INFO,"   %c  Start smartd in debug Mode\n",DEBUGMODE);
-  printout(LOG_INFO,"   %c  Print License, Copyright, and version information\n",PRINTCOPYLEFT);
+  printout(LOG_INFO,"   %c  Print License, Copyright, and version information\n\n",PRINTCOPYLEFT);
+  printout(LOG_INFO,"Configuration file: /etc/smartd.conf\n");
 }
 	
 // scan to see what ata devices there are, and if they support SMART
-void atadevicescan ( atadevices_t *devices){
-  int i;
+int atadevicescan (atadevices_t *devices, char *device){
   int fd;
   struct hd_driveid drive;
-  char device[] = "/dev/hda";
   
-  for(i=0;i<MAXATADEVICES;i++,device[7]++ ){
-    
-    printout(LOG_INFO,"Reading Device %s\n", device);
-    
-    fd = open(device, O_RDONLY);
-    if (fd < 0)
-      // no such device
-      continue;
-    
-    if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
-      // device exists, but not able to do SMART
-      close(fd);
-      printout(LOG_INFO,"Device: %s, Found but not SMART capable, or couldn't enable SMART\n",device);
-      continue;
-    }
-
-    // Does device support read values and read thresholds?  We should
-    // modify this next block for devices that do support SMART status
-    // but don't support read values and read thresholds.
-    if (ataReadSmartValues (fd,&devices[numatadevices].smartval)){
-      close(fd);
-      printout(LOG_INFO,"Device: %s, Read SMART Values Failed\n",device);
-      continue;
-    }
-    else if (ataReadSmartThresholds (fd,&devices[numatadevices].smartthres)){
-      close(fd);
-      printout(LOG_INFO,"Device: %s, Read SMART Thresholds Failed\n",device);
-      continue;
-    }
-
-    // device exists, and does SMART.  Add to list
-    printout(LOG_INFO,"%s Found and is SMART capable\n",device);
-    devices[numatadevices].fd = fd;
-    strcpy(devices[numatadevices].devicename, device);
-    devices[numatadevices].drive = drive;
-        
-    // This makes NO sense.  We may want to know if the drive supports
-    // Offline Surface Scan, for example.  But checking if it supports
-    // self-tests seems useless. In any case, smartd NEVER uses this
-    // field anywhere...
-    devices[numatadevices].selftest = 
-      isSupportSelfTest(devices[numatadevices].smartval);
-
-    numatadevices++;
+  printout(LOG_INFO,"Opening device %s\n", device);
+  fd = open(device, O_RDONLY);
+  if (fd < 0) {
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
+    else
+      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+    return 1;
+  }
+  
+  if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
+    // device exists, but not able to do SMART
+    close(fd);
+    printout(LOG_INFO,"Device: %s, Found but not SMART capable, or couldn't enable SMART\n",device);
+    return 2;
+  }
+  
+  // Does device support read values and read thresholds?  We should
+  // modify this next block for devices that do support SMART status
+  // but don't support read values and read thresholds.
+  if (ataReadSmartValues (fd,&devices[numatadevices].smartval)){
+    close(fd);
+    printout(LOG_INFO,"Device: %s, Read SMART Values Failed\n",device);
+    return 3;
   }
+  else if (ataReadSmartThresholds (fd,&devices[numatadevices].smartthres)){
+    close(fd);
+    printout(LOG_INFO,"Device: %s, Read SMART Thresholds Failed\n",device);
+    return 4;
+  }
+  
+  // device exists, and does SMART.  Add to list
+  printout(LOG_INFO,"%s Found and is SMART capable. Adding to \"monitor\" list.\n",device);
+  devices[numatadevices].fd = fd;
+  strcpy(devices[numatadevices].devicename, device);
+  devices[numatadevices].drive = drive;
+  
+  // This makes NO sense.  We may want to know if the drive supports
+  // Offline Surface Scan, for example.  But checking if it supports
+  // self-tests seems useless. In any case, smartd NEVER uses this
+  // field anywhere...
+  devices[numatadevices].selftest = 
+    isSupportSelfTest(devices[numatadevices].smartval);
+  
+  numatadevices++;
+  return 0;
 }
 
-
-
 // This function is hard to read and ought to be rewritten. Why in the
 // world is the four-byte integer cast to a pointer to an eight-byte
 // object??
-void scsidevicescan ( scsidevices_t *devices){
+int scsidevicescan (scsidevices_t *devices, char *device){
   int i, fd, smartsupport;
   unsigned char  tBuf[4096];
-  char device[] = "/dev/sda";	
   
-  for(i = 0; i < MAXSCSIDEVICES ; i++,device[7]++ ){
-    
-    printout(LOG_INFO,"Reading Device %s\n", device);
-    
-    fd=open (device, O_RDWR);
-    
-    if (fd<0)
-      continue;
+  
+  // open device
+  printout(LOG_INFO,"Opening device %s\n", device);
+  fd=open(device, O_RDWR);
+  if (fd<0) {
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
+    else
+      printout(LOG_INFO,"Device: %s, Opening device failed\n", device);
+    return 1;
+  }
+
+  // check that it's ready for commands
+  if (!testunitready(fd)){
+    printout(LOG_INFO,"Device: %s, Failed Test Unit Ready\n", device);
+    close(fd);
+    return 2;
+  }
+  
+  // make sure that we can read mode page
+  if (modesense(fd, 0x1c, (UINT8 *) &tBuf)){
+    printout(LOG_INFO,"Device: %s, Failed read of ModePage 0x1c\n", device);
+    close(fd);
+    return 3;
+  }
+  
+  // see if SMART is supported and enabled
+  if (scsiSmartSupport(fd, (UINT8 *) &smartsupport) ||
+      (smartsupport & DEXCPT_ENABLE)){
+    printout(LOG_INFO,"Device: %s, SMART not supported or not enabled\n", device);
+    close(fd);
+    return 4;
+  }
 
-    if (!testunitready (fd)) {
-      if (modesense(fd, 0x1c, (UINT8 *) &tBuf)){
-	printout(LOG_INFO,"Device: %s, Failed read of ModePage 1C \n", device);
-	close(fd);
+  // now we can proceed to register the device
+  printout(LOG_INFO, "Device: %s, Found and is SMART capable. Adding to \"monitor\" list.\n",device);
+  devices[numscsidevices].fd = fd;
+  strcpy(devices[numscsidevices].devicename,device);
+
+  // register the supported functionality.  The smartd code does not
+  // seem to make any further use of this information.
+  if (logsense(fd, SUPPORT_LOG_PAGES, (UINT8 *) &tBuf) == 0){
+    for ( i = 4; i < tBuf[3] + LOGPAGEHDRSIZE ; i++){
+      switch ( tBuf[i]){ 
+      case TEMPERATURE_PAGE:
+	devices[numscsidevices].TempPageSupported = 1;
+	break;
+      case SMART_PAGE:
+	devices[numscsidevices].SmartPageSupported = 1;
+	break;
+      default:
+	break;
       }
-      else
-	if (!scsiSmartSupport( fd, (UINT8 *) &smartsupport) &&
-	    !(smartsupport & DEXCPT_ENABLE)){
-	  devices[numscsidevices].fd = fd;
-	  strcpy(devices[numscsidevices].devicename,device);
-	  
-	  printout(LOG_INFO, "Device: %s, Found and is SMART capable\n",device);
-	  
-	  if (logsense ( fd , SUPPORT_LOG_PAGES, (UINT8 *) &tBuf) == 0){
-	    for ( i = 4; i < tBuf[3] + LOGPAGEHDRSIZE ; i++){
-	      switch ( tBuf[i]){ 
-	      case TEMPERATURE_PAGE:
-		devices[numscsidevices].TempPageSupported = 1;
-		break;
-	      case SMART_PAGE:
-		devices[numscsidevices].SmartPageSupported = 1;
-		break;
-	      default:
-		break;
-	      }
-	    }	
-	  }
-	  numscsidevices++;
-	}
-	else
-	  close(fd);
-    }
+    }	
   }
+  numscsidevices++;
+  return 0;
 }
 
 
@@ -295,8 +308,8 @@ int ataCheckDevice( atadevices_t *drive){
 }
 
 
-int scsiCheckDevice( scsidevices_t *drive)
-{
+
+int scsiCheckDevice( scsidevices_t *drive){
   UINT8 returnvalue;
   UINT8 currenttemp;
   UINT8 triptemp;
@@ -312,6 +325,8 @@ int scsiCheckDevice( scsidevices_t *drive)
   else
     printout(LOG_INFO,"Device: %s, Acceptable attribute: %d\n", drive->devicename, returnvalue);  
   
+  // Seems to completely ignore what capabilities were found on the
+  // device when scanned
   if (currenttemp){
     if ( (currenttemp != drive->Temperature) && ( drive->Temperature) )
       printout(LOG_INFO, "Device: %s, Temperature changed %d degrees to %d degrees since last reading\n", 
@@ -414,23 +429,111 @@ char copyleftstring[]=
 "under the terms of the GNU General Public License Version 2.\n"
 "See http://www.gnu.org for further details.\n\n";
 
-const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'\0' };
+cfgfile config[MAXENTRIES];
+
+
+// returns number of entries in config file, or 0 if no config file exists
+int parseconfigfile(){
+  FILE *fp;
+  int entry=0,lineno=0;
+  char line[MAXLINELEN+2];
+  
+  // Open config file, if it exists
+  fp=fopen(CONFIGFILE,"r");
+  if (fp==NULL && errno!=ENOENT){
+    // file exists but we can't read it
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Unable to open configuration file %s\n",
+	       sys_errlist[errno],CONFIGFILE);
+    else
+      printout(LOG_INFO,"Unable to open configuration file %s\n",CONFIGFILE);
+    exit(1);
+  }
+  
+  // No config file
+  if (fp==NULL)
+    return 0;
+  
+  // configuration file exists.  Read it and search for devices
+  printout(LOG_INFO,"Using configuration file %s\n",CONFIGFILE);
+  while (fgets(line,MAXLINELEN+2,fp)){
+    int len;
+    char *dev;
+    
+    // track linenumber for error messages
+    lineno++;
+    
+    // See if line is too long
+    len=strlen(line);
+    if (len>MAXLINELEN){
+      printout(LOG_INFO,"Error: line %d of file %s is more than than %d characters long.\n",
+	       lineno,CONFIGFILE,MAXLINELEN);
+      exit(1); 
+    }
+    
+    // eliminate any terminating newline
+    if (line[len-1]=='\n'){
+      len--;
+      line[len]='\0';
+    }
+    
+    // Skip white space
+    dev=line;
+    while (*dev && (*dev==' ' || *dev=='\t'))
+      dev++;
+    len=strlen(dev);
+    
+    // If line is blank, or a comment, skip it
+    if (!len || *dev=='#')
+      continue;
+    
+    // We've got a legit entry
+    if (entry>=MAXENTRIES){
+      printout(LOG_INFO,"Error: configuration file %s can have no more than %d entries\n",
+	       CONFIGFILE,MAXENTRIES);
+      exit(1);
+    }
+    
+    // Copy information into data structure for after forking
+    strcpy(config[entry].name,dev);
+    config[entry].lineno=lineno;
+    config[entry].tryscsi=config[entry].tryata=1;
+    
+    // Try and recognize if a IDE or SCSI device
+    if (len>5 && !strncmp("/dev/h",dev, 6))
+      config[entry].tryscsi=0;
+    
+    if (len>5 && !strncmp("/dev/s",dev, 6))
+      config[entry].tryata=0;
+    
+    entry++;
+  }
+  fclose(fp);
+  if (entry)
+    return entry;
+  
+  printout(LOG_INFO,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
+  exit(1);
+}
+
+const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'h','?','\0' };
 
 /* Main Program */
 int main (int argc, char **argv){
-  
   atadevices_t atadevices[MAXATADEVICES], *atadevicesptr;
   scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr;
-  int optchar;
+  int optchar,i;
   extern char *optarg;
   extern int  optopt, optind, opterr;
+  int entries;
   
   numatadevices=0;
   numscsidevices=0;
   scsidevicesptr = scsidevices;
   atadevicesptr = atadevices;
-  opterr=1;
+  opterr=optopt=0;
 
+  // Parse input options:
   while (-1 != (optchar = getopt(argc, argv, opts))){
     switch(optchar) {
     case PRINTCOPYLEFT:
@@ -443,14 +546,22 @@ int main (int argc, char **argv){
       emailnotification = TRUE;
       break;
     case '?':
+    case 'h':
     default:
       debugmode=1;
-      printout(LOG_INFO,"\n");
+      if (optopt) {
+	printhead();
+	printout(LOG_INFO,"=======> UNRECOGNIZED OPTION: %c <======= \n\n",optopt);
+	Usage();
+	exit(-1);
+      }
+      printhead();
       Usage();
-      exit(-1);	
+      exit(0);
     }
   }
   
+  // If needed print copyright, license and version information
   if (printcopyleft){
     debugmode=1;
     printhead();
@@ -462,20 +573,39 @@ int main (int argc, char **argv){
     exit(0);
   }
   
+  // print header
   printhead();
  
+  // look in configuration file CONFIGFILE (normally /etc/smartd.conf)
+  entries=parseconfigfile();
 
-   // If we are running in background as a daemon, call 
-  // a routines that forks then closes file descriptors. 
+  // If in background as a daemon, fork and close file descriptors
   if (!debugmode){
     daemon_init();
   }
- 
-  // routines that look for devices on ata and scsi bus 
-  atadevicescan (atadevicesptr); 
-  scsidevicescan (scsidevicesptr);
   
-  CheckDevices ( atadevicesptr, scsidevicesptr); 
+  // If we found a config file, look at its entries
+  if (entries)
+    for (i=0;i<entries;i++){
+      if (config[i].tryata && atadevicescan(atadevicesptr, config[i].name))
+	printout(LOG_INFO,"Unable to register ATA device %s at line %d of file %s\n",
+		 config[i].name, config[i].lineno, CONFIGFILE);
+      
+      if (config[i].tryscsi && scsidevicescan(scsidevicesptr, config[i].name))
+	printout(LOG_INFO,"Unable to register SCSI device %s at line %d of file %s\n",
+		 config[i].name, config[i].lineno, CONFIGFILE);
+    }
+  else {
+    char deviceata[] = "/dev/hda";
+    char devicescsi[]= "/dev/sda";
+    printout(LOG_INFO,"No configuration file %s found. Searching for devices.\n",CONFIGFILE);
+    for(i=0;i<MAXATADEVICES;i++,deviceata[7]++)
+      atadevicescan(atadevicesptr, deviceata);    
+    for(i=0;i<MAXSCSIDEVICES;i++,devicescsi[7]++)
+      scsidevicescan(scsidevicesptr, devicescsi);
+  }
+  
+  CheckDevices(atadevicesptr, scsidevicesptr); 
   return 0;
 }
 
diff --git a/sm5/smartd.h b/sm5/smartd.h
index 0e6c13577e6b31a7631506986d42ac4b0b7a5ebc..43d9a1b0e95b758720b9ee042f8c1fda2a8d6793 100644
--- a/sm5/smartd.h
+++ b/sm5/smartd.h
@@ -23,9 +23,14 @@
  */
 
 #ifndef CVSID7
-#define CVSID7 "$Id: smartd.h,v 1.7 2002/10/24 07:50:45 ballen4705 Exp $\n"
+#define CVSID7 "$Id: smartd.h,v 1.8 2002/10/25 14:15:05 ballen4705 Exp $\n"
 #endif
 
+// Configuration file
+#define CONFIGFILE "/etc/smartd.conf"
+#define MAXLINELEN 126
+#define MAXENTRIES 64
+
 /* Defines for command line options */ 
 #define DEBUGMODE 		'X'
 #define EMAILNOTIFICATION	'e'
@@ -67,3 +72,10 @@ typedef struct scsidevices_s {
 	unsigned char TempPageSupported;
 	unsigned char Temperature;
 } scsidevices_t;
+
+typedef struct configfile_s {
+  char name[MAXLINELEN+2];
+  int tryata;
+  int tryscsi;
+  int lineno;
+} cfgfile;