From 97a9c2f1827f3eb5c2841720fcda53db676e9607 Mon Sep 17 00:00:00 2001
From: ballen4705 <ballen4705@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Mon, 28 Oct 2002 23:47:00 +0000
Subject: [PATCH] This is the first CVS checkin of code that extends the
 options available for smartd. The following options can be placed into the
 /etc/smartd.conf file, and control the behavior of smartd.

Configuration file Directives (following device name):
  -A    Device is an ATA device
  -S    Device is a SCSI device
  -c    Monitor SMART Health Status
  -l    Monitor SMART Error Log for changes
  -L    Monitor SMART Self-Test Log for new errors
  -f    Monitor for failure of any 'Usage' Attributes
  -p    Report changes in 'Prefailure' Attributes
  -u    Report changes in 'Usage' Attributes
  -t    Equivalent to -p and -u Directives
  -i ID Ignore Attribute ID for -f Directive
  -I ID Ignore Attribute ID for -p, -u or -t Directive
   #    Comment: text after a hash sign is ignored
   \    Line continuation character
Attribute ID is a decimal integer 1 <= ID <= 255
All but -S directive are only implemented for ATA devices
Example: /dev/hda -a

This code is still very experimental and needs some testing before I generate a release.


git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@147 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/Makefile      |  18 +-
 sm5/atacmds.c     |  46 ++-
 sm5/atacmds.cpp   |  46 ++-
 sm5/atacmds.h     |  15 +-
 sm5/ataprint.c    | 115 +++---
 sm5/ataprint.cpp  | 115 +++---
 sm5/ataprint.h    |  28 +-
 sm5/extern.h      |  54 +--
 sm5/scsicmds.c    |   2 +-
 sm5/scsicmds.cpp  |   2 +-
 sm5/scsiprint.c   |  29 +-
 sm5/scsiprint.cpp |  29 +-
 sm5/scsiprint.h   |   6 +-
 sm5/smartctl.8    |  19 +-
 sm5/smartctl.c    | 134 ++++---
 sm5/smartctl.cpp  | 134 ++++---
 sm5/smartd.8      | 261 ++++++++++---
 sm5/smartd.c      | 908 +++++++++++++++++++++++++++++++++++-----------
 sm5/smartd.conf   |  48 ++-
 sm5/smartd.cpp    | 908 +++++++++++++++++++++++++++++++++++-----------
 sm5/smartd.h      |  59 ++-
 21 files changed, 2125 insertions(+), 851 deletions(-)

diff --git a/sm5/Makefile b/sm5/Makefile
index 3b7f6c5ff..7150c8663 100644
--- a/sm5/Makefile
+++ b/sm5/Makefile
@@ -2,7 +2,7 @@
 #
 # Home page: http://smartmontools.sourceforge.net
 #
-# $Id: Makefile,v 1.24 2002/10/26 20:53:08 ballen4705 Exp $
+# $Id: Makefile,v 1.25 2002/10/28 23:46:59 ballen4705 Exp $
 #
 # Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 # 
@@ -23,13 +23,13 @@
 CC	= gcc
 
 # Debugging
-# CFLAGS = -fsigned-char -Wall -g
+CFLAGS = -fsigned-char -Wall -g
 
 # Build against kernel header files.  Change linux-2.4 to correct path for your system
 # CFLAGS	= -fsigned-char -Wall -O2 -I./usr/src/linux-2.4/include
 
 # Normal build
-CFLAGS	= -fsigned-char -Wall -O2 
+# CFLAGS	= -fsigned-char -Wall -O2 
 
 releasefiles=atacmds.c atacmds.h ataprint.c ataprint.h CHANGELOG COPYING extern.h Makefile\
   README scsicmds.c scsicmds.h scsiprint.c scsiprint.h smartctl.8 smartctl.c smartctl.h\
@@ -42,22 +42,22 @@ pkgname2=$(pkgname)-$(counter)
 
 all: smartd smartctl
 
-smartctl: atacmds.o scsicmds.o smartctl.c smartctl.h ataprint.o scsiprint.o atacmds.h ataprint.h scsicmds.h scsiprint.h VERSION Makefile
+smartctl: smartctl.c atacmds.o ataprint.o scsicmds.o scsiprint.o atacmds.h ataprint.h extern.h scsicmds.h scsiprint.h smartctl.h  VERSION Makefile
 	${CC} -DSMARTMONTOOLS_VERSION=$(counter) -o smartctl ${CFLAGS} smartctl.c atacmds.o scsicmds.o ataprint.o scsiprint.o
 
-smartd:  atacmds.o scsicmds.o smartd.c smartd.h atacmds.h scsicmds.h VERSION Makefile
-	${CC} -DSMARTMONTOOLS_VERSION=$(counter) -o smartd ${CFLAGS} smartd.c scsicmds.o atacmds.o
+smartd:  smartd.c atacmds.o ataprint.o scsicmds.o atacmds.h ataprint.h extern.h scsicmds.h smartd.h  VERSION Makefile
+	${CC} -DSMARTMONTOOLS_VERSION=$(counter) -o smartd ${CFLAGS} smartd.c scsicmds.o atacmds.o ataprint.o
 
 atacmds.o: atacmds.h atacmds.c Makefile
 	${CC} ${CFLAGS} -c atacmds.c 
 
-ataprint.o: atacmds.o ataprint.h ataprint.c smartctl.h extern.h Makefile
+ataprint.o: ataprint.c atacmds.h ataprint.h smartctl.h extern.h Makefile
 	${CC} ${CFLAGS} -c ataprint.c
 
-scsicmds.o: scsicmds.h scsicmds.c Makefile
+scsicmds.o: scsicmds.c scsicmds.h Makefile
 	${CC} ${CFLAGS} -c scsicmds.c
 
-scsiprint.o: scsiprint.h scsiprint.c scsicmds.o smartctl.h extern.h scsicmds.h Makefile
+scsiprint.o: scsiprint.c  extern.h scsicmds.h scsiprint.h smartctl.h Makefile
 	${CC} ${CFLAGS} -c scsiprint.c 
 
 clean:
diff --git a/sm5/atacmds.c b/sm5/atacmds.c
index cebba28fc..b19ec3d8c 100644
--- a/sm5/atacmds.c
+++ b/sm5/atacmds.c
@@ -30,7 +30,7 @@
 #include <stdlib.h>
 #include "atacmds.h"
 
-const char *CVSid1="$Id: atacmds.c,v 1.30 2002/10/26 20:10:34 ballen4705 Exp $" CVSID1;
+const char *CVSid1="$Id: atacmds.c,v 1.31 2002/10/28 23:46:59 ballen4705 Exp $" CVSID1;
 
 // These Drive Identity tables are taken from hdparm 5.2, and are also
 // given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
@@ -632,11 +632,11 @@ int isSupportSelfTest (struct ata_smart_values data){
 // condition where imminent loss of data is being predicted."
 
 
-// onlyfailing=0 : are or were any age or prefailure attributes <= threshold
-// onlyfailing=1:  are any prefailure attributes <= threshold now
-int ataCheckSmart (struct ata_smart_values data,
-		   struct ata_smart_thresholds thresholds,
-		   int onlyfailed){
+// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
+// onlyfailed=1:  are any prefailure attributes <= threshold now
+int ataCheckSmart(struct ata_smart_values data,
+		  struct ata_smart_thresholds thresholds,
+		  int onlyfailed){
   int i;
   
   // loop over all attributes
@@ -664,6 +664,40 @@ int ataCheckSmart (struct ata_smart_values data,
 }
 
 
+
+// This checks the n'th attribute in the attribute list, NOT the
+// attribute with id==n.  If the attribute does not exist, or the
+// attribute is > threshold, then returns zero.  If the attribute is
+// <= threshold (failing) then we the attribute number if it is a
+// prefail attribute.  Else we return minus the attribute number if it
+// is a usage attribute.
+int ataCheckAttribute(struct ata_smart_values *data,
+		      struct ata_smart_thresholds *thresholds,
+		      int n){
+  struct ata_smart_attribute *disk;
+  struct ata_smart_threshold_entry *thre;
+  
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  disk=data->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  if (!disk || !thre)
+    return 0;
+  
+  // consider only valid attributes, check for failure
+  if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold)
+    return 0;
+  
+  // We have found a failed attribute.  Return positive or negative? 
+  if (disk->status.flag.prefailure)
+    return disk->id;
+  else
+    return -1*(disk->id);
+}
+
 // Note some attribute names appear redundant because different
 // manufacturers use different attribute IDs for an attribute with the
 // same name.
diff --git a/sm5/atacmds.cpp b/sm5/atacmds.cpp
index 92f46e9d1..22e9afc28 100644
--- a/sm5/atacmds.cpp
+++ b/sm5/atacmds.cpp
@@ -30,7 +30,7 @@
 #include <stdlib.h>
 #include "atacmds.h"
 
-const char *CVSid1="$Id: atacmds.cpp,v 1.30 2002/10/26 20:10:34 ballen4705 Exp $" CVSID1;
+const char *CVSid1="$Id: atacmds.cpp,v 1.31 2002/10/28 23:46:59 ballen4705 Exp $" CVSID1;
 
 // These Drive Identity tables are taken from hdparm 5.2, and are also
 // given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
@@ -632,11 +632,11 @@ int isSupportSelfTest (struct ata_smart_values data){
 // condition where imminent loss of data is being predicted."
 
 
-// onlyfailing=0 : are or were any age or prefailure attributes <= threshold
-// onlyfailing=1:  are any prefailure attributes <= threshold now
-int ataCheckSmart (struct ata_smart_values data,
-		   struct ata_smart_thresholds thresholds,
-		   int onlyfailed){
+// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
+// onlyfailed=1:  are any prefailure attributes <= threshold now
+int ataCheckSmart(struct ata_smart_values data,
+		  struct ata_smart_thresholds thresholds,
+		  int onlyfailed){
   int i;
   
   // loop over all attributes
@@ -664,6 +664,40 @@ int ataCheckSmart (struct ata_smart_values data,
 }
 
 
+
+// This checks the n'th attribute in the attribute list, NOT the
+// attribute with id==n.  If the attribute does not exist, or the
+// attribute is > threshold, then returns zero.  If the attribute is
+// <= threshold (failing) then we the attribute number if it is a
+// prefail attribute.  Else we return minus the attribute number if it
+// is a usage attribute.
+int ataCheckAttribute(struct ata_smart_values *data,
+		      struct ata_smart_thresholds *thresholds,
+		      int n){
+  struct ata_smart_attribute *disk;
+  struct ata_smart_threshold_entry *thre;
+  
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  disk=data->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  if (!disk || !thre)
+    return 0;
+  
+  // consider only valid attributes, check for failure
+  if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold)
+    return 0;
+  
+  // We have found a failed attribute.  Return positive or negative? 
+  if (disk->status.flag.prefailure)
+    return disk->id;
+  else
+    return -1*(disk->id);
+}
+
 // Note some attribute names appear redundant because different
 // manufacturers use different attribute IDs for an attribute with the
 // same name.
diff --git a/sm5/atacmds.h b/sm5/atacmds.h
index 439e45f68..6285b6d2b 100644
--- a/sm5/atacmds.h
+++ b/sm5/atacmds.h
@@ -26,7 +26,7 @@
 #define _ATACMDS_H_
 
 #ifndef CVSID1
-#define CVSID1 "$Id: atacmds.h,v 1.20 2002/10/26 19:59:01 ballen4705 Exp $\n"
+#define CVSID1 "$Id: atacmds.h,v 1.21 2002/10/28 23:46:59 ballen4705 Exp $\n"
 #endif
 
 // These are the major and minor versions for smartd and smartctl
@@ -384,8 +384,15 @@ void pout(char *fmt, ...);
 #define CVSMAXLEN 512
 void printone(char *block, const char *cvsid);
 
-// MACROS to control printing behavior
-#define QUIETON  {if (quietmode) veryquietmode=0;}
-#define QUIETOFF {if (quietmode && !veryquietmode) veryquietmode=1;}
+
+// This checks the n'th attribute in the attribute list, NOT the
+// attribute with id==n.  If the attribute does not exist, or the
+// attribute is > threshold, then returns zero.  If the attribute is
+// <= threshold (failing) then we the attribute number if it is a
+// prefail attribute.  Else we return minus the attribute number if it
+// is a usage attribute.
+int ataCheckAttribute(struct ata_smart_values *data,
+		      struct ata_smart_thresholds *thresholds,
+		      int n);
 
 #endif /* _ATACMDS_H_ */
diff --git a/sm5/ataprint.c b/sm5/ataprint.c
index 5b99788f6..e2e471fce 100644
--- a/sm5/ataprint.c
+++ b/sm5/ataprint.c
@@ -24,12 +24,16 @@
 
 #include <ctype.h>
 #include <stdio.h>
+#include "atacmds.h"
 #include "ataprint.h"
 #include "smartctl.h"
 #include "extern.h"
 
-const char *CVSid4="$Id: ataprint.c,v 1.34 2002/10/26 19:59:01 ballen4705 Exp $"
-CVSID2 CVSID3 CVSID6;
+const char *CVSid2="$Id: ataprint.c,v 1.35 2002/10/28 23:46:59 ballen4705 Exp $"
+CVSID1 CVSID2 CVSID3 CVSID6;
+
+// for passing global control variables
+extern atamainctrl *con;
 
 // Function for printing ASCII byte-swapped strings, skipping white
 // space. This is needed on little-endian architectures, eg Intel,
@@ -420,7 +424,7 @@ void PrintSmartAttribWithThres (struct ata_smart_values data,
       switch (disk->id){
 	// Power on time
       case 9:
-	if (smart009minutes)
+	if (con->smart009minutes)
 	  // minutes
 	  pout ("%llu h + %2llu m\n",rawvalue/60,rawvalue%60);
 	else
@@ -494,7 +498,8 @@ int nonempty(unsigned char *testarea,int n){
       return 1;
   return 0;
 }
-  
+
+// returns number of errors
 void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
   int i,j,k;
   
@@ -505,7 +510,7 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
     pout ("No Errors Logged\n\n");
     return;
   }
-  QUIETON;
+  QUIETON(con);
   // if log pointer out of range, return
   if ( data.error_log_pointer>5 ){
     pout("Invalid Error Log index = %02x (T13/1321D rev 1c"
@@ -520,7 +525,7 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
   else
     pout ( "ATA Error Count: %u (device log contains only the most recent five errors)\n",
 	   data.ata_error_count);
-  QUIETOFF;
+  QUIETOFF(con);
   pout("\tDCR = Device Control Register\n");
   pout("\tFR  = Features Register\n");
   pout("\tSC  = Sector Count Register\n");
@@ -552,10 +557,10 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
       default:   msgstate="in a vendor specific or reserved state";
       }
       // See table 42 of ATA5 spec
-      QUIETON;
+      QUIETON(con);
       pout("Error %i occurred at disk power-on lifetime: %u hours\n",
 	     5-k,data.errorlog_struct[i].error_struct.timestamp);
-      QUIETOFF;
+      QUIETOFF(con);
       pout("When the command that caused the error occurred, the device was %s.\n",msgstate);
       pout("After command completion occurred, registers were:\n");
       pout("ER:%02x SC:%02x SN:%02x CL:%02x CH:%02x D/H:%02x ST:%02x\n",
@@ -588,15 +593,15 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
       pout("\n");
     }
   }
-  QUIETON;
-  if (quietmode)
+  QUIETON(con);
+  if (con->quietmode)
     pout("\n");
-  QUIETOFF;
+  QUIETOFF(con);
   return;  
 }
 
 // return value is number of entries found where the self-test showed an error
-int ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data,int allentries){
+int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog data,int allentries){
   int i,j,noheaderprinted=1;
   int retval=0;
 
@@ -701,7 +706,7 @@ struct ata_smart_selftestlog smartselftest;
 int ataPrintMain (int fd){
   int timewait,code;
   int returnval=0;
-  
+
   // Start by getting Drive ID information.  We need this, to know if SMART is supported.
   if (ataReadHDIdentity(fd,&drive)){
     pout("Smartctl: Hard Drive Read Identity Failed\n\n");
@@ -709,7 +714,7 @@ int ataPrintMain (int fd){
   }
   
   // Print most drive identity information if requested
-  if (driveinfo){
+  if (con->driveinfo){
     pout("=== START OF INFORMATION SECTION ===\n");
     ataPrintDriveInfo(drive);
   }
@@ -724,11 +729,11 @@ int ataPrintMain (int fd){
     }
     else
       pout("                  SMART appears to work.  Continuing.\n"); 
-    if (!driveinfo) pout("\n");
+    if (!con->driveinfo) pout("\n");
   }
   
   // Now print remaining drive info: is SMART enabled?    
-  if (driveinfo){
+  if (con->driveinfo){
     pout("SMART support is: Available - device has SMART capability.\n");
     if (ataDoesSmartWork(fd))
       pout("SMART support is: Enabled\n");
@@ -739,13 +744,13 @@ int ataPrintMain (int fd){
 
   
   // START OF THE ENABLE/DISABLE SECTION OF THE CODE
-  if (smartenable || smartdisable || 
-      smartautosaveenable || smartautosavedisable || 
-      smartautoofflineenable || smartautoofflinedisable)
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
     pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
   
   // Enable/Disable SMART commands
-  if (smartenable){
+  if (con->smartenable){
     if (ataEnableSmart(fd)) {
       pout("Smartctl: SMART Enable Failed.\n\n");
       returnval|=FAILSMART;
@@ -761,7 +766,7 @@ int ataPrintMain (int fd){
   }
   
   // Turn off SMART on device
-  if (smartdisable){    
+  if (con->smartdisable){    
     if (ataDisableSmart(fd)) {
       pout( "Smartctl: SMART Disable Failed.\n\n");
       returnval|=FAILSMART;
@@ -776,7 +781,7 @@ int ataPrintMain (int fd){
     returnval|=FAILSMART;
   
   // Enable/Disable Auto-save attributes
-  if (smartautosaveenable){
+  if (con->smartautosaveenable){
     if (ataEnableAutoSave(fd)){
       pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
       returnval|=FAILSMART;
@@ -784,7 +789,7 @@ int ataPrintMain (int fd){
     else
       pout("SMART Attribute Autosave Enabled.\n");
   }
-  if (smartautosavedisable){
+  if (con->smartautosavedisable){
     if (ataDisableAutoSave(fd)){
       pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
       returnval|=FAILSMART;
@@ -804,7 +809,7 @@ int ataPrintMain (int fd){
   }
 
   // Enable/Disable Off-line testing
-  if (smartautoofflineenable){
+  if (con->smartautoofflineenable){
     if (!isSupportAutomaticTimer(smartval)){
       pout("Warning: device does not support SMART Automatic Timers.\n\n");
     }
@@ -815,7 +820,7 @@ int ataPrintMain (int fd){
     else
       pout ("SMART Automatic Offline Testing Enabled every four hours.\n");
   }
-  if (smartautoofflinedisable){
+  if (con->smartautoofflinedisable){
     if (!isSupportAutomaticTimer(smartval)){
       pout("Warning: device does not support SMART Automatic Timers.\n\n");
     }
@@ -828,28 +833,28 @@ int ataPrintMain (int fd){
   }
 
   // all this for a newline!
-  if (smartenable || smartdisable || 
-      smartautosaveenable || smartautosavedisable || 
-      smartautoofflineenable || smartautoofflinedisable)
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
     pout("\n");
 
   // START OF READ-ONLY OPTIONS APART FROM -V and -i
-  if (checksmart || generalsmartvalues || smartvendorattrib || smarterrorlog || smartselftestlog)
+  if (con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog || con->smartselftestlog)
     pout("=== START OF READ SMART DATA SECTION ===\n");
   
   // Check SMART status (use previously returned value)
-  if (checksmart){
+  if (con->checksmart){
     if (code) {
-      QUIETON;
+      QUIETON(con);
       pout("SMART overall-health self-assessment test result: FAILED!\n"
 	     "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
-      QUIETOFF;
+      QUIETOFF(con);
       if (ataCheckSmart(smartval, smartthres,1)){
 	returnval|=FAILATTR;
-	if (smartvendorattrib)
+	if (con->smartvendorattrib)
 	  pout("See vendor-specific Attribute list for failed Attributes.\n\n");
 	else {
-	  QUIETON;
+	  QUIETON(con);
 	  pout("Failed Attributes:\n");
 	  PrintSmartAttribWithThres(smartval, smartthres,1);
 	}
@@ -857,15 +862,15 @@ int ataPrintMain (int fd){
       else
 	pout("No failed Attributes found.\n\n");   
       returnval|=FAILSTATUS;
-      QUIETOFF;
+      QUIETOFF(con);
     }
     else {
       pout("SMART overall-health self-assessment test result: PASSED\n");
       if (ataCheckSmart(smartval, smartthres,0)){
-	if (smartvendorattrib)
+	if (con->smartvendorattrib)
 	  pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
 	else {
-	  QUIETON;
+	  QUIETON(con);
 	  pout("Please note the following marginal attributes:\n");
 	  PrintSmartAttribWithThres(smartval, smartthres,2);
 	} 
@@ -874,22 +879,22 @@ int ataPrintMain (int fd){
       else
 	pout("\n");
     }
-    QUIETOFF;
+    QUIETOFF(con);
   }
   
   // Print general SMART values
-  if (generalsmartvalues)
+  if (con->generalsmartvalues)
     ataPrintGeneralSmartValues(smartval); 
   
   // Print vendor-specific attributes
-  if (smartvendorattrib){
-    QUIETON;
-    PrintSmartAttribWithThres(smartval, smartthres,quietmode?2:0);
-    QUIETOFF;
+  if (con->smartvendorattrib){
+    QUIETON(con);
+    PrintSmartAttribWithThres(smartval, smartthres,con->quietmode?2:0);
+    QUIETOFF(con);
   }
   
   // Print SMART error log
-  if (smarterrorlog){
+  if (con->smarterrorlog){
     if (!isSmartErrorLogCapable(smartval))
       pout("Warning: device does not support Error Logging\n");
     if (ataReadErrorLog(fd, &smarterror)){
@@ -899,12 +904,12 @@ int ataPrintMain (int fd){
     else {
       // turn on quiet mode inside this
       ataPrintSmartErrorlog(smarterror);
-      QUIETOFF;
+      QUIETOFF(con);
     }
   }
   
   // Print SMART self-test log
-  if (smartselftestlog){
+  if (con->smartselftestlog){
     if (!isSmartErrorLogCapable(smartval))
       pout("Warning: device does not support Self Test Logging\n");
     else {
@@ -913,36 +918,36 @@ int ataPrintMain (int fd){
 	returnval|=FAILSMART;
       }
       else {
-	QUIETON;
-	if (ataPrintSmartSelfTestlog(smartselftest,!quietmode))
+	QUIETON(con);
+	if (ataPrintSmartSelfTestlog(smartselftest,!con->quietmode))
 	  returnval|=FAILLOG;
-	QUIETOFF;
+	QUIETOFF(con);
 	pout("\n");
       }
     } 
   }
   
   // START OF THE TESTING SECTION OF THE CODE.  IF NO TESTING, RETURN
-  if (testcase==-1)
+  if (con->testcase==-1)
     return returnval;
   
   pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n");
   // if doing a self-test, be sure it's supported by the hardware
-  if (testcase==OFFLINE_FULL_SCAN &&  !isSupportExecuteOfflineImmediate(smartval))
+  if (con->testcase==OFFLINE_FULL_SCAN &&  !isSupportExecuteOfflineImmediate(smartval))
     pout("Warning: device does not support Execute Off-Line Immediate function.\n\n");
   else if (!isSupportSelfTest(smartval))
     pout ("Warning: device does not support Self-Test functions.\n\n");
   
   // Now do the test
-  if (ataSmartTest(fd, testcase))
+  if (ataSmartTest(fd, con->testcase))
     return returnval|=FAILSMART;
   
   // Tell user how long test will take to complete  
-  if ((timewait=TestTime(smartval,testcase))){ 
+  if ((timewait=TestTime(smartval,con->testcase))){ 
     pout ("Please wait %d %s for test to complete.\n",
-	    timewait, testcase==OFFLINE_FULL_SCAN?"seconds":"minutes");
+	    timewait, con->testcase==OFFLINE_FULL_SCAN?"seconds":"minutes");
     
-    if (testcase!=SHORT_CAPTIVE_SELF_TEST && testcase!=EXTEND_CAPTIVE_SELF_TEST)
+    if (con->testcase!=SHORT_CAPTIVE_SELF_TEST && con->testcase!=EXTEND_CAPTIVE_SELF_TEST)
       pout ("Use smartctl -%c to abort test.\n", SMARTSELFTESTABORT);	
   }    
   return returnval;
diff --git a/sm5/ataprint.cpp b/sm5/ataprint.cpp
index 2802fe6ab..2e332784c 100644
--- a/sm5/ataprint.cpp
+++ b/sm5/ataprint.cpp
@@ -24,12 +24,16 @@
 
 #include <ctype.h>
 #include <stdio.h>
+#include "atacmds.h"
 #include "ataprint.h"
 #include "smartctl.h"
 #include "extern.h"
 
-const char *CVSid4="$Id: ataprint.cpp,v 1.34 2002/10/26 19:59:01 ballen4705 Exp $"
-CVSID2 CVSID3 CVSID6;
+const char *CVSid2="$Id: ataprint.cpp,v 1.35 2002/10/28 23:46:59 ballen4705 Exp $"
+CVSID1 CVSID2 CVSID3 CVSID6;
+
+// for passing global control variables
+extern atamainctrl *con;
 
 // Function for printing ASCII byte-swapped strings, skipping white
 // space. This is needed on little-endian architectures, eg Intel,
@@ -420,7 +424,7 @@ void PrintSmartAttribWithThres (struct ata_smart_values data,
       switch (disk->id){
 	// Power on time
       case 9:
-	if (smart009minutes)
+	if (con->smart009minutes)
 	  // minutes
 	  pout ("%llu h + %2llu m\n",rawvalue/60,rawvalue%60);
 	else
@@ -494,7 +498,8 @@ int nonempty(unsigned char *testarea,int n){
       return 1;
   return 0;
 }
-  
+
+// returns number of errors
 void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
   int i,j,k;
   
@@ -505,7 +510,7 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
     pout ("No Errors Logged\n\n");
     return;
   }
-  QUIETON;
+  QUIETON(con);
   // if log pointer out of range, return
   if ( data.error_log_pointer>5 ){
     pout("Invalid Error Log index = %02x (T13/1321D rev 1c"
@@ -520,7 +525,7 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
   else
     pout ( "ATA Error Count: %u (device log contains only the most recent five errors)\n",
 	   data.ata_error_count);
-  QUIETOFF;
+  QUIETOFF(con);
   pout("\tDCR = Device Control Register\n");
   pout("\tFR  = Features Register\n");
   pout("\tSC  = Sector Count Register\n");
@@ -552,10 +557,10 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
       default:   msgstate="in a vendor specific or reserved state";
       }
       // See table 42 of ATA5 spec
-      QUIETON;
+      QUIETON(con);
       pout("Error %i occurred at disk power-on lifetime: %u hours\n",
 	     5-k,data.errorlog_struct[i].error_struct.timestamp);
-      QUIETOFF;
+      QUIETOFF(con);
       pout("When the command that caused the error occurred, the device was %s.\n",msgstate);
       pout("After command completion occurred, registers were:\n");
       pout("ER:%02x SC:%02x SN:%02x CL:%02x CH:%02x D/H:%02x ST:%02x\n",
@@ -588,15 +593,15 @@ void ataPrintSmartErrorlog (struct ata_smart_errorlog data){
       pout("\n");
     }
   }
-  QUIETON;
-  if (quietmode)
+  QUIETON(con);
+  if (con->quietmode)
     pout("\n");
-  QUIETOFF;
+  QUIETOFF(con);
   return;  
 }
 
 // return value is number of entries found where the self-test showed an error
-int ataPrintSmartSelfTestlog (struct ata_smart_selftestlog data,int allentries){
+int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog data,int allentries){
   int i,j,noheaderprinted=1;
   int retval=0;
 
@@ -701,7 +706,7 @@ struct ata_smart_selftestlog smartselftest;
 int ataPrintMain (int fd){
   int timewait,code;
   int returnval=0;
-  
+
   // Start by getting Drive ID information.  We need this, to know if SMART is supported.
   if (ataReadHDIdentity(fd,&drive)){
     pout("Smartctl: Hard Drive Read Identity Failed\n\n");
@@ -709,7 +714,7 @@ int ataPrintMain (int fd){
   }
   
   // Print most drive identity information if requested
-  if (driveinfo){
+  if (con->driveinfo){
     pout("=== START OF INFORMATION SECTION ===\n");
     ataPrintDriveInfo(drive);
   }
@@ -724,11 +729,11 @@ int ataPrintMain (int fd){
     }
     else
       pout("                  SMART appears to work.  Continuing.\n"); 
-    if (!driveinfo) pout("\n");
+    if (!con->driveinfo) pout("\n");
   }
   
   // Now print remaining drive info: is SMART enabled?    
-  if (driveinfo){
+  if (con->driveinfo){
     pout("SMART support is: Available - device has SMART capability.\n");
     if (ataDoesSmartWork(fd))
       pout("SMART support is: Enabled\n");
@@ -739,13 +744,13 @@ int ataPrintMain (int fd){
 
   
   // START OF THE ENABLE/DISABLE SECTION OF THE CODE
-  if (smartenable || smartdisable || 
-      smartautosaveenable || smartautosavedisable || 
-      smartautoofflineenable || smartautoofflinedisable)
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
     pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
   
   // Enable/Disable SMART commands
-  if (smartenable){
+  if (con->smartenable){
     if (ataEnableSmart(fd)) {
       pout("Smartctl: SMART Enable Failed.\n\n");
       returnval|=FAILSMART;
@@ -761,7 +766,7 @@ int ataPrintMain (int fd){
   }
   
   // Turn off SMART on device
-  if (smartdisable){    
+  if (con->smartdisable){    
     if (ataDisableSmart(fd)) {
       pout( "Smartctl: SMART Disable Failed.\n\n");
       returnval|=FAILSMART;
@@ -776,7 +781,7 @@ int ataPrintMain (int fd){
     returnval|=FAILSMART;
   
   // Enable/Disable Auto-save attributes
-  if (smartautosaveenable){
+  if (con->smartautosaveenable){
     if (ataEnableAutoSave(fd)){
       pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
       returnval|=FAILSMART;
@@ -784,7 +789,7 @@ int ataPrintMain (int fd){
     else
       pout("SMART Attribute Autosave Enabled.\n");
   }
-  if (smartautosavedisable){
+  if (con->smartautosavedisable){
     if (ataDisableAutoSave(fd)){
       pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
       returnval|=FAILSMART;
@@ -804,7 +809,7 @@ int ataPrintMain (int fd){
   }
 
   // Enable/Disable Off-line testing
-  if (smartautoofflineenable){
+  if (con->smartautoofflineenable){
     if (!isSupportAutomaticTimer(smartval)){
       pout("Warning: device does not support SMART Automatic Timers.\n\n");
     }
@@ -815,7 +820,7 @@ int ataPrintMain (int fd){
     else
       pout ("SMART Automatic Offline Testing Enabled every four hours.\n");
   }
-  if (smartautoofflinedisable){
+  if (con->smartautoofflinedisable){
     if (!isSupportAutomaticTimer(smartval)){
       pout("Warning: device does not support SMART Automatic Timers.\n\n");
     }
@@ -828,28 +833,28 @@ int ataPrintMain (int fd){
   }
 
   // all this for a newline!
-  if (smartenable || smartdisable || 
-      smartautosaveenable || smartautosavedisable || 
-      smartautoofflineenable || smartautoofflinedisable)
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
     pout("\n");
 
   // START OF READ-ONLY OPTIONS APART FROM -V and -i
-  if (checksmart || generalsmartvalues || smartvendorattrib || smarterrorlog || smartselftestlog)
+  if (con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog || con->smartselftestlog)
     pout("=== START OF READ SMART DATA SECTION ===\n");
   
   // Check SMART status (use previously returned value)
-  if (checksmart){
+  if (con->checksmart){
     if (code) {
-      QUIETON;
+      QUIETON(con);
       pout("SMART overall-health self-assessment test result: FAILED!\n"
 	     "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
-      QUIETOFF;
+      QUIETOFF(con);
       if (ataCheckSmart(smartval, smartthres,1)){
 	returnval|=FAILATTR;
-	if (smartvendorattrib)
+	if (con->smartvendorattrib)
 	  pout("See vendor-specific Attribute list for failed Attributes.\n\n");
 	else {
-	  QUIETON;
+	  QUIETON(con);
 	  pout("Failed Attributes:\n");
 	  PrintSmartAttribWithThres(smartval, smartthres,1);
 	}
@@ -857,15 +862,15 @@ int ataPrintMain (int fd){
       else
 	pout("No failed Attributes found.\n\n");   
       returnval|=FAILSTATUS;
-      QUIETOFF;
+      QUIETOFF(con);
     }
     else {
       pout("SMART overall-health self-assessment test result: PASSED\n");
       if (ataCheckSmart(smartval, smartthres,0)){
-	if (smartvendorattrib)
+	if (con->smartvendorattrib)
 	  pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
 	else {
-	  QUIETON;
+	  QUIETON(con);
 	  pout("Please note the following marginal attributes:\n");
 	  PrintSmartAttribWithThres(smartval, smartthres,2);
 	} 
@@ -874,22 +879,22 @@ int ataPrintMain (int fd){
       else
 	pout("\n");
     }
-    QUIETOFF;
+    QUIETOFF(con);
   }
   
   // Print general SMART values
-  if (generalsmartvalues)
+  if (con->generalsmartvalues)
     ataPrintGeneralSmartValues(smartval); 
   
   // Print vendor-specific attributes
-  if (smartvendorattrib){
-    QUIETON;
-    PrintSmartAttribWithThres(smartval, smartthres,quietmode?2:0);
-    QUIETOFF;
+  if (con->smartvendorattrib){
+    QUIETON(con);
+    PrintSmartAttribWithThres(smartval, smartthres,con->quietmode?2:0);
+    QUIETOFF(con);
   }
   
   // Print SMART error log
-  if (smarterrorlog){
+  if (con->smarterrorlog){
     if (!isSmartErrorLogCapable(smartval))
       pout("Warning: device does not support Error Logging\n");
     if (ataReadErrorLog(fd, &smarterror)){
@@ -899,12 +904,12 @@ int ataPrintMain (int fd){
     else {
       // turn on quiet mode inside this
       ataPrintSmartErrorlog(smarterror);
-      QUIETOFF;
+      QUIETOFF(con);
     }
   }
   
   // Print SMART self-test log
-  if (smartselftestlog){
+  if (con->smartselftestlog){
     if (!isSmartErrorLogCapable(smartval))
       pout("Warning: device does not support Self Test Logging\n");
     else {
@@ -913,36 +918,36 @@ int ataPrintMain (int fd){
 	returnval|=FAILSMART;
       }
       else {
-	QUIETON;
-	if (ataPrintSmartSelfTestlog(smartselftest,!quietmode))
+	QUIETON(con);
+	if (ataPrintSmartSelfTestlog(smartselftest,!con->quietmode))
 	  returnval|=FAILLOG;
-	QUIETOFF;
+	QUIETOFF(con);
 	pout("\n");
       }
     } 
   }
   
   // START OF THE TESTING SECTION OF THE CODE.  IF NO TESTING, RETURN
-  if (testcase==-1)
+  if (con->testcase==-1)
     return returnval;
   
   pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n");
   // if doing a self-test, be sure it's supported by the hardware
-  if (testcase==OFFLINE_FULL_SCAN &&  !isSupportExecuteOfflineImmediate(smartval))
+  if (con->testcase==OFFLINE_FULL_SCAN &&  !isSupportExecuteOfflineImmediate(smartval))
     pout("Warning: device does not support Execute Off-Line Immediate function.\n\n");
   else if (!isSupportSelfTest(smartval))
     pout ("Warning: device does not support Self-Test functions.\n\n");
   
   // Now do the test
-  if (ataSmartTest(fd, testcase))
+  if (ataSmartTest(fd, con->testcase))
     return returnval|=FAILSMART;
   
   // Tell user how long test will take to complete  
-  if ((timewait=TestTime(smartval,testcase))){ 
+  if ((timewait=TestTime(smartval,con->testcase))){ 
     pout ("Please wait %d %s for test to complete.\n",
-	    timewait, testcase==OFFLINE_FULL_SCAN?"seconds":"minutes");
+	    timewait, con->testcase==OFFLINE_FULL_SCAN?"seconds":"minutes");
     
-    if (testcase!=SHORT_CAPTIVE_SELF_TEST && testcase!=EXTEND_CAPTIVE_SELF_TEST)
+    if (con->testcase!=SHORT_CAPTIVE_SELF_TEST && con->testcase!=EXTEND_CAPTIVE_SELF_TEST)
       pout ("Use smartctl -%c to abort test.\n", SMARTSELFTESTABORT);	
   }    
   return returnval;
diff --git a/sm5/ataprint.h b/sm5/ataprint.h
index c58112908..8bff3e511 100644
--- a/sm5/ataprint.h
+++ b/sm5/ataprint.h
@@ -26,35 +26,41 @@
 #define _SMART_PRINT_H_
 
 #ifndef CVSID2
-#define CVSID2 "$Id: ataprint.h,v 1.12 2002/10/24 09:54:02 ballen4705 Exp $\n"
+#define CVSID2 "$Id: ataprint.h,v 1.13 2002/10/28 23:46:59 ballen4705 Exp $\n"
 #endif
 
 #include <stdio.h>
 #include <stdlib.h>
-#include "atacmds.h"
+
+// MACROS to control printing behavior
+#define QUIETON(control)  {if (control->quietmode) control->veryquietmode=0;}
+#define QUIETOFF(control) {if (control->quietmode && !control->veryquietmode) control->veryquietmode=1;}
+
+
 
 
 /* Prints ATA Drive Information and S.M.A.R.T. Capability */
-void ataPrintDriveInfo (struct hd_driveid);
+void ataPrintDriveInfo(struct hd_driveid);
 
-void ataPrintGeneralSmartValues (struct ata_smart_values);
+void ataPrintGeneralSmartValues(struct ata_smart_values);
 
-void ataPrintSmartThresholds (struct ata_smart_thresholds);
+void ataPrintSmartThresholds(struct ata_smart_thresholds);
 
-void ataPrintSmartErrorlog (struct ata_smart_errorlog);
+void ataPrintSmartErrorlog(struct ata_smart_errorlog);
 
-void PrintSmartAttributes (struct ata_smart_values data);
+void PrintSmartAttributes(struct ata_smart_values data);
 
-void PrintSmartAttribWithThres (struct ata_smart_values data,
+void PrintSmartAttribWithThres(struct ata_smart_values data,
                                 struct ata_smart_thresholds thresholds,
 				int onlyfailed);
 
 // returns number of entries that had logged errors
 int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog data, int allentries);
 
-void ataPseudoCheckSmart (struct ata_smart_values , 
-                          struct ata_smart_thresholds );
+void ataPseudoCheckSmart(struct ata_smart_values, struct ata_smart_thresholds );
+
+
 
-int ataPrintMain ( int fd );
+int ataPrintMain(int fd);
 
 #endif
diff --git a/sm5/extern.h b/sm5/extern.h
index d9b0f9ee4..85ebca773 100644
--- a/sm5/extern.h
+++ b/sm5/extern.h
@@ -27,30 +27,36 @@
 
 
 #ifndef CVSID3
-#define CVSID3 "$Id: extern.h,v 1.8 2002/10/23 12:24:24 ballen4705 Exp $\n"
+#define CVSID3 "$Id: extern.h,v 1.9 2002/10/28 23:46:59 ballen4705 Exp $\n"
 #endif
 
-extern unsigned char driveinfo;
-extern unsigned char checksmart;
-extern unsigned char smartvendorattrib;
-extern unsigned char generalsmartvalues;
-extern unsigned char smartselftestlog;
-extern unsigned char smarterrorlog;
-extern unsigned char smartdisable;
-extern unsigned char smartenable; 
-extern unsigned char smartstatus;
-extern unsigned char smartexeoffimmediate;
-extern unsigned char smartshortselftest;
-extern unsigned char smartextendselftest;
-extern unsigned char smartshortcapselftest;
-extern unsigned char smartextendcapselftest;
-extern unsigned char smartselftestabort;
-extern unsigned char smartautoofflineenable;
-extern unsigned char smartautoofflinedisable;
-extern unsigned char smartautosaveenable;
-extern unsigned char smartautosavedisable;
-extern unsigned char smart009minutes;
-extern int           testcase;
-extern unsigned char quietmode;
-extern unsigned char veryquietmode;
+// Block used for global control/communications.  If you need more
+// global variables, this should be the only place that you need to
+// add them.
+typedef struct ataprintmain_s {
+  unsigned char driveinfo;
+  unsigned char checksmart;
+  unsigned char smartvendorattrib;
+  unsigned char generalsmartvalues;
+  unsigned char smartselftestlog;
+  unsigned char smarterrorlog;
+  unsigned char smartdisable;
+  unsigned char smartenable; 
+  unsigned char smartstatus;
+  unsigned char smartexeoffimmediate;
+  unsigned char smartshortselftest;
+  unsigned char smartextendselftest;
+  unsigned char smartshortcapselftest;
+  unsigned char smartextendcapselftest;
+  unsigned char smartselftestabort;
+  unsigned char smartautoofflineenable;
+  unsigned char smartautoofflinedisable;
+  unsigned char smartautosaveenable;
+  unsigned char smartautosavedisable;
+  unsigned char smart009minutes;
+  int           testcase;
+  unsigned char quietmode;
+  unsigned char veryquietmode;
+} atamainctrl;
+
 #endif
diff --git a/sm5/scsicmds.c b/sm5/scsicmds.c
index cad4705a2..12184f2ca 100644
--- a/sm5/scsicmds.c
+++ b/sm5/scsicmds.c
@@ -33,7 +33,7 @@
 #include <scsi/scsi.h>
 #include "scsicmds.h"
 
-const char *CVSid2="$Id: scsicmds.c,v 1.11 2002/10/23 20:36:59 ballen4705 Exp $" CVSID4;
+const char *CVSid3="$Id: scsicmds.c,v 1.12 2002/10/28 23:46:59 ballen4705 Exp $" CVSID4;
 
 
 UINT8 logsense (int device, UINT8 pagenum, UINT8 *pBuf)
diff --git a/sm5/scsicmds.cpp b/sm5/scsicmds.cpp
index 9ef4b0f19..65ef61b19 100644
--- a/sm5/scsicmds.cpp
+++ b/sm5/scsicmds.cpp
@@ -33,7 +33,7 @@
 #include <scsi/scsi.h>
 #include "scsicmds.h"
 
-const char *CVSid2="$Id: scsicmds.cpp,v 1.11 2002/10/23 20:36:59 ballen4705 Exp $" CVSID4;
+const char *CVSid3="$Id: scsicmds.cpp,v 1.12 2002/10/28 23:46:59 ballen4705 Exp $" CVSID4;
 
 
 UINT8 logsense (int device, UINT8 pagenum, UINT8 *pBuf)
diff --git a/sm5/scsiprint.c b/sm5/scsiprint.c
index d459b84db..47446e8c8 100644
--- a/sm5/scsiprint.c
+++ b/sm5/scsiprint.c
@@ -30,15 +30,18 @@
 #include <errno.h>
 
 #include "smartctl.h"
-#include "extern.h"
 #include "scsicmds.h"
 #include "scsiprint.h"
+#include "extern.h"
 
 #define GBUF_SIZE 65535
 
-const char* CVSid5="$Id: scsiprint.c,v 1.9 2002/10/23 20:36:59 ballen4705 Exp $"
+const char* CVSid4="$Id: scsiprint.c,v 1.10 2002/10/28 23:46:59 ballen4705 Exp $"
 CVSID3 CVSID4 CVSID5 CVSID6;
 
+// control block which points to external global control variables
+extern atamainctrl *con;
+
 UINT8 gBuf[GBUF_SIZE];
 
 UINT8 gSmartPage = 0;
@@ -293,20 +296,20 @@ void scsiPrintStopStart ( int device )
   printf ("Start Stop Count: %d\n", css);
 **/
 }
- 
+
 void scsiPrintMain (int fd)
 {
 
-    if (driveinfo)
+    if (con->driveinfo)
 	scsiGetDriveInfo(fd); 
 
-    if (smartenable) 
+    if (con->smartenable) 
 	scsiSmartEnable(fd);
 
-    if (smartdisable)
+    if (con->smartdisable)
 	scsiSmartDisable(fd);
 
-    if (checksmart)
+    if (con->checksmart)
     {
 	scsiGetSupportPages (fd);
         if(gTapeAlertsPage)
@@ -322,7 +325,7 @@ void scsiPrintMain (int fd)
     }	
 
 	
-    if ( smartexeoffimmediate )
+    if ( con->smartexeoffimmediate )
     {
 	if ( scsiSmartOfflineTest (fd) != 0) 
 	{
@@ -336,7 +339,7 @@ void scsiPrintMain (int fd)
     }
 
 
-    if ( smartshortcapselftest )
+    if ( con->smartshortcapselftest )
     {
 	if ( scsiSmartShortCapSelfTest (fd) != 0) 
 	{
@@ -347,7 +350,7 @@ void scsiPrintMain (int fd)
         printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);	
    }
 
-   if ( smartshortselftest )
+   if ( con->smartshortselftest )
    { 
 		
       if ( scsiSmartShortSelfTest (fd) != 0) 
@@ -359,7 +362,7 @@ void scsiPrintMain (int fd)
         printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);
    }
 	
-   if ( smartextendselftest )
+   if ( con->smartextendselftest )
    {
       if ( scsiSmartExtendSelfTest (fd) != 0) 
       {
@@ -371,7 +374,7 @@ void scsiPrintMain (int fd)
    printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);	
    }
 	
-	if ( smartextendcapselftest )
+	if ( con->smartextendcapselftest )
 	{
 		
 		if ( scsiSmartExtendCapSelfTest (fd) != 0) 
@@ -384,7 +387,7 @@ void scsiPrintMain (int fd)
         printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);	
 	}
 
-	if ( smartselftestabort )
+	if ( con->smartselftestabort )
 	{
 		
 		if ( scsiSmartSelfTestAbort (fd) != 0) 
diff --git a/sm5/scsiprint.cpp b/sm5/scsiprint.cpp
index 288d67f99..77049a2bf 100644
--- a/sm5/scsiprint.cpp
+++ b/sm5/scsiprint.cpp
@@ -30,15 +30,18 @@
 #include <errno.h>
 
 #include "smartctl.h"
-#include "extern.h"
 #include "scsicmds.h"
 #include "scsiprint.h"
+#include "extern.h"
 
 #define GBUF_SIZE 65535
 
-const char* CVSid5="$Id: scsiprint.cpp,v 1.9 2002/10/23 20:36:59 ballen4705 Exp $"
+const char* CVSid4="$Id: scsiprint.cpp,v 1.10 2002/10/28 23:46:59 ballen4705 Exp $"
 CVSID3 CVSID4 CVSID5 CVSID6;
 
+// control block which points to external global control variables
+extern atamainctrl *con;
+
 UINT8 gBuf[GBUF_SIZE];
 
 UINT8 gSmartPage = 0;
@@ -293,20 +296,20 @@ void scsiPrintStopStart ( int device )
   printf ("Start Stop Count: %d\n", css);
 **/
 }
- 
+
 void scsiPrintMain (int fd)
 {
 
-    if (driveinfo)
+    if (con->driveinfo)
 	scsiGetDriveInfo(fd); 
 
-    if (smartenable) 
+    if (con->smartenable) 
 	scsiSmartEnable(fd);
 
-    if (smartdisable)
+    if (con->smartdisable)
 	scsiSmartDisable(fd);
 
-    if (checksmart)
+    if (con->checksmart)
     {
 	scsiGetSupportPages (fd);
         if(gTapeAlertsPage)
@@ -322,7 +325,7 @@ void scsiPrintMain (int fd)
     }	
 
 	
-    if ( smartexeoffimmediate )
+    if ( con->smartexeoffimmediate )
     {
 	if ( scsiSmartOfflineTest (fd) != 0) 
 	{
@@ -336,7 +339,7 @@ void scsiPrintMain (int fd)
     }
 
 
-    if ( smartshortcapselftest )
+    if ( con->smartshortcapselftest )
     {
 	if ( scsiSmartShortCapSelfTest (fd) != 0) 
 	{
@@ -347,7 +350,7 @@ void scsiPrintMain (int fd)
         printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);	
    }
 
-   if ( smartshortselftest )
+   if ( con->smartshortselftest )
    { 
 		
       if ( scsiSmartShortSelfTest (fd) != 0) 
@@ -359,7 +362,7 @@ void scsiPrintMain (int fd)
         printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);
    }
 	
-   if ( smartextendselftest )
+   if ( con->smartextendselftest )
    {
       if ( scsiSmartExtendSelfTest (fd) != 0) 
       {
@@ -371,7 +374,7 @@ void scsiPrintMain (int fd)
    printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);	
    }
 	
-	if ( smartextendcapselftest )
+	if ( con->smartextendcapselftest )
 	{
 		
 		if ( scsiSmartExtendCapSelfTest (fd) != 0) 
@@ -384,7 +387,7 @@ void scsiPrintMain (int fd)
         printf ("Use smartctl -%c to abort test\n", SMARTSELFTESTABORT);	
 	}
 
-	if ( smartselftestabort )
+	if ( con->smartselftestabort )
 	{
 		
 		if ( scsiSmartSelfTestAbort (fd) != 0) 
diff --git a/sm5/scsiprint.h b/sm5/scsiprint.h
index 07b02c0f7..6a1b21084 100644
--- a/sm5/scsiprint.h
+++ b/sm5/scsiprint.h
@@ -28,11 +28,9 @@
 #define SCSI_PRINT_H_
 
 #ifndef CVSID5
-#define CVSID5 "$Id: scsiprint.h,v 1.6 2002/10/22 09:44:55 ballen4705 Exp $\n"
+#define CVSID5 "$Id: scsiprint.h,v 1.7 2002/10/28 23:46:59 ballen4705 Exp $\n"
 #endif
 
-void scsiPrintMain (int fd);
-
-
+void scsiPrintMain(int fd);
 
 #endif
diff --git a/sm5/smartctl.8 b/sm5/smartctl.8
index 02a3ffb9c..f7b94878c 100644
--- a/sm5/smartctl.8
+++ b/sm5/smartctl.8
@@ -1,6 +1,6 @@
 \# Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 \#
-\# $Id: smartctl.8,v 1.20 2002/10/24 09:54:02 ballen4705 Exp $
+\# $Id: smartctl.8,v 1.21 2002/10/28 23:46:59 ballen4705 Exp $
 \# 
 \# 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
@@ -15,7 +15,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 SMARTCTL 8  "$Date: 2002/10/24 09:54:02 $" "smartmontools-5.0"
+.TH SMARTCTL 8  "$Date: 2002/10/28 23:46:59 $" "smartmontools-5.0"
 .SH NAME
 smartctl \- S.M.A.R.T. control and monitor utility 
 .SH SYNOPSIS
@@ -384,6 +384,19 @@ The device error log contains records of errors.
 .TP
 .B Bit 7:
 The device self-test log contains records of errors.
+
+To test within the shell for whether or not the different bits are
+turned on or off, you can use the following type of construction (this
+is bash syntax):
+.nf
+.B smartstat=$(($? & 8))
+.fi
+This looks at only at bit 3 of the exit status
+.B $?
+(since 8=2^3).  The shell variable
+$smartstat will be nonzero if SMART status check returned 'disk
+failing' and zero otherwise.
+
 .PP
 .SH AUTHOR
 Bruce Allen
@@ -458,4 +471,4 @@ Please let us know if there is an on\-line source for this document.
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartctl.8,v 1.20 2002/10/24 09:54:02 ballen4705 Exp $
+$Id: smartctl.8,v 1.21 2002/10/28 23:46:59 ballen4705 Exp $
diff --git a/sm5/smartctl.c b/sm5/smartctl.c
index bfec094cd..f030db67a 100644
--- a/sm5/smartctl.c
+++ b/sm5/smartctl.c
@@ -36,39 +36,19 @@
 #include "ataprint.h"
 #include "scsicmds.h"
 #include "scsiprint.h"
+#include "extern.h"
 
-extern const char *CVSid1, *CVSid2, *CVSid4, *CVSid5; 
-const char* CVSid6="$Id: smartctl.c,v 1.21 2002/10/26 19:33:40 ballen4705 Exp $"
-CVSID1 CVSID2 CVSID4 CVSID5 CVSID6;
-
-unsigned char driveinfo               = FALSE;
-unsigned char checksmart              = FALSE;
-unsigned char smartvendorattrib       = FALSE;
-unsigned char generalsmartvalues      = FALSE;
-unsigned char smartselftestlog        = FALSE;
-unsigned char smarterrorlog           = FALSE;
-unsigned char smartdisable            = FALSE;
-unsigned char smartenable             = FALSE;
-unsigned char smartstatus             = FALSE;
-unsigned char smartexeoffimmediate    = FALSE;
-unsigned char smartshortselftest      = FALSE;
-unsigned char smartextendselftest     = FALSE;
-unsigned char smartshortcapselftest   = FALSE;
-unsigned char smartextendcapselftest  = FALSE;
-unsigned char smartselftestabort      = FALSE;
-unsigned char smartautoofflineenable  = FALSE;
-unsigned char smartautoofflinedisable = FALSE;
-unsigned char smartautosaveenable     = FALSE;
-unsigned char smartautosavedisable    = FALSE;
-unsigned char printcopyleft           = FALSE;
-unsigned char smart009minutes         = FALSE;
-unsigned char quietmode               = FALSE;
-unsigned char veryquietmode           = FALSE;
-int           testcase                = -1;
+extern const char *CVSid1, *CVSid2, *CVSid3, *CVSid4; 
+const char* CVSid5="$Id: smartctl.c,v 1.22 2002/10/28 23:46:59 ballen4705 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",RELEASE_MAJOR,RELEASE_MINOR,SMARTMONTOOLS_VERSION);
+  pout("smartctl version %d.%d-%d Copyright (C) 2002 Bruce Allen\n",
+       RELEASE_MAJOR,RELEASE_MINOR,SMARTMONTOOLS_VERSION);
   pout("Home page is %s\n\n",PROJECTHOME);
   return;
 }
@@ -81,12 +61,12 @@ void printcopy(){
   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,CVSid6);
-  pout("%s",out);
   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);
@@ -140,99 +120,103 @@ const char opts[] = {
   SMARTAUTOSAVEENABLE,SMARTAUTOSAVEDISABLE,PRINTCOPYLEFT,SMART009MINUTES,QUIETMODE,VERYQUIETMODE,'h','?','\0'
 };
 
+unsigned char printcopyleft=0;
+
 /*      Takes command options and sets features to be run */	
 void ParseOpts (int argc, char** argv){
   int optchar;
   extern char *optarg;
   extern int optopt, optind, opterr;
   
+  memset(con,0,sizeof(*con));
+  con->testcase=-1;
   opterr=optopt=0;
   while (-1 != (optchar = getopt(argc, argv, opts))) {
     switch (optchar){
     case QUIETMODE:
-      quietmode=TRUE;
+      con->quietmode=TRUE;
       break;
     case VERYQUIETMODE:
-      veryquietmode=TRUE;
+      con->veryquietmode=TRUE;
       break;
     case SMART009MINUTES:
-      smart009minutes=TRUE;
+      con->smart009minutes=TRUE;
       break;
     case PRINTCOPYLEFT :
       printcopyleft=TRUE;
       break;
     case DRIVEINFO :
-      driveinfo  = TRUE;
+      con->driveinfo  = TRUE;
       break;		
     case CHECKSMART :
-      checksmart = TRUE;		
+      con->checksmart = TRUE;		
       break;
     case SMARTVERBOSEALL :
-      driveinfo = TRUE;
-      checksmart = TRUE;
-      generalsmartvalues = TRUE;
-      smartvendorattrib = TRUE;
-      smarterrorlog = TRUE;
-      smartselftestlog = TRUE;
+      con->driveinfo = TRUE;
+      con->checksmart = TRUE;
+      con->generalsmartvalues = TRUE;
+      con->smartvendorattrib = TRUE;
+      con->smarterrorlog = TRUE;
+      con->smartselftestlog = TRUE;
       break;
     case SMARTVENDORATTRIB :
-      smartvendorattrib = TRUE;
+      con->smartvendorattrib = TRUE;
       break;
     case GENERALSMARTVALUES :
-      generalsmartvalues = TRUE;
+      con->generalsmartvalues = TRUE;
       break;
     case SMARTERRORLOG :
-      smarterrorlog = TRUE;
+      con->smarterrorlog = TRUE;
       break;
     case SMARTSELFTESTLOG :
-      smartselftestlog = TRUE;
+      con->smartselftestlog = TRUE;
       break;
     case SMARTDISABLE :
-      smartdisable = TRUE;
+      con->smartdisable = TRUE;
       break;
     case SMARTENABLE :
-      smartenable   = TRUE;
+      con->smartenable   = TRUE;
       break;
     case SMARTAUTOSAVEENABLE:
-      smartautosaveenable = TRUE;
+      con->smartautosaveenable = TRUE;
       break;
     case SMARTAUTOSAVEDISABLE:
-      smartautosavedisable = TRUE;
+      con->smartautosavedisable = TRUE;
       break;
     case SMARTAUTOOFFLINEENABLE: 
-      smartautoofflineenable = TRUE;
+      con->smartautoofflineenable = TRUE;
       break;
     case SMARTAUTOOFFLINEDISABLE:
-      smartautoofflinedisable = TRUE;
+      con->smartautoofflinedisable = TRUE;
       break;
     case SMARTEXEOFFIMMEDIATE:
-      smartexeoffimmediate = TRUE;
-      testcase=OFFLINE_FULL_SCAN;
+      con->smartexeoffimmediate = TRUE;
+      con->testcase=OFFLINE_FULL_SCAN;
       break;
     case SMARTSHORTSELFTEST :
-      smartshortselftest = TRUE;
-      testcase=SHORT_SELF_TEST;
+      con->smartshortselftest = TRUE;
+      con->testcase=SHORT_SELF_TEST;
       break;
     case SMARTEXTENDSELFTEST :
-      smartextendselftest = TRUE;
-      testcase=EXTEND_SELF_TEST;
+      con->smartextendselftest = TRUE;
+      con->testcase=EXTEND_SELF_TEST;
       break;
     case SMARTSHORTCAPSELFTEST:
-      smartshortcapselftest = TRUE;
-      testcase=SHORT_CAPTIVE_SELF_TEST;
+      con->smartshortcapselftest = TRUE;
+      con->testcase=SHORT_CAPTIVE_SELF_TEST;
       break;
     case SMARTEXTENDCAPSELFTEST:
-      smartextendcapselftest = TRUE;
-      testcase=EXTEND_CAPTIVE_SELF_TEST;
+      con->smartextendcapselftest = TRUE;
+      con->testcase=EXTEND_CAPTIVE_SELF_TEST;
       break;
     case SMARTSELFTESTABORT:
-      smartselftestabort = TRUE;
-      testcase=ABORT_SELF_TEST;
+      con->smartselftestabort = TRUE;
+      con->testcase=ABORT_SELF_TEST;
       break;
     case 'h':
     case '?':
     default:
-      veryquietmode=FALSE;
+      con->veryquietmode=FALSE;
       printslogan();
       if (optopt){
 	pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
@@ -244,13 +228,13 @@ void ParseOpts (int argc, char** argv){
     }
   }
   // Do this here, so results are independent of argument order	
-  if (quietmode)
-    veryquietmode=TRUE;
+  if (con->quietmode)
+    con->veryquietmode=TRUE;
   
   // error message if user has asked for more than one test
-  if (1<(smartexeoffimmediate+smartshortselftest+smartextendselftest+
-	 smartshortcapselftest+smartextendcapselftest+smartselftestabort)){
-    veryquietmode=FALSE;
+  if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+
+	 con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort)){
+    con->veryquietmode=FALSE;
     printslogan();
     Usage();
     printf ("\nERROR: smartctl can only run a single test (or abort) at a time.\n\n");
@@ -269,13 +253,13 @@ void ParseOpts (int argc, char** argv){
 }
 
 
-// Printing function (controlled by global veryquietmode)
+// Printing function (controlled by global con->veryquietmode)
 void pout(char *fmt, ...){
   va_list ap;
 
   // initialize variable argument list 
   va_start(ap,fmt);
-  if (veryquietmode){
+  if (con->veryquietmode){
     va_end(ap);
     return;
   }
@@ -291,7 +275,11 @@ void pout(char *fmt, ...){
 int main (int argc, char **argv){
   int fd,retval=0;
   char *device;
-  
+  atamainctrl control;
+
+  // define control block for external functions
+  con=&control;
+
   // Part input arguments
   ParseOpts(argc,argv);
     
diff --git a/sm5/smartctl.cpp b/sm5/smartctl.cpp
index 904421e37..26e39dbef 100644
--- a/sm5/smartctl.cpp
+++ b/sm5/smartctl.cpp
@@ -36,39 +36,19 @@
 #include "ataprint.h"
 #include "scsicmds.h"
 #include "scsiprint.h"
+#include "extern.h"
 
-extern const char *CVSid1, *CVSid2, *CVSid4, *CVSid5; 
-const char* CVSid6="$Id: smartctl.cpp,v 1.21 2002/10/26 19:33:40 ballen4705 Exp $"
-CVSID1 CVSID2 CVSID4 CVSID5 CVSID6;
-
-unsigned char driveinfo               = FALSE;
-unsigned char checksmart              = FALSE;
-unsigned char smartvendorattrib       = FALSE;
-unsigned char generalsmartvalues      = FALSE;
-unsigned char smartselftestlog        = FALSE;
-unsigned char smarterrorlog           = FALSE;
-unsigned char smartdisable            = FALSE;
-unsigned char smartenable             = FALSE;
-unsigned char smartstatus             = FALSE;
-unsigned char smartexeoffimmediate    = FALSE;
-unsigned char smartshortselftest      = FALSE;
-unsigned char smartextendselftest     = FALSE;
-unsigned char smartshortcapselftest   = FALSE;
-unsigned char smartextendcapselftest  = FALSE;
-unsigned char smartselftestabort      = FALSE;
-unsigned char smartautoofflineenable  = FALSE;
-unsigned char smartautoofflinedisable = FALSE;
-unsigned char smartautosaveenable     = FALSE;
-unsigned char smartautosavedisable    = FALSE;
-unsigned char printcopyleft           = FALSE;
-unsigned char smart009minutes         = FALSE;
-unsigned char quietmode               = FALSE;
-unsigned char veryquietmode           = FALSE;
-int           testcase                = -1;
+extern const char *CVSid1, *CVSid2, *CVSid3, *CVSid4; 
+const char* CVSid5="$Id: smartctl.cpp,v 1.22 2002/10/28 23:46:59 ballen4705 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",RELEASE_MAJOR,RELEASE_MINOR,SMARTMONTOOLS_VERSION);
+  pout("smartctl version %d.%d-%d Copyright (C) 2002 Bruce Allen\n",
+       RELEASE_MAJOR,RELEASE_MINOR,SMARTMONTOOLS_VERSION);
   pout("Home page is %s\n\n",PROJECTHOME);
   return;
 }
@@ -81,12 +61,12 @@ void printcopy(){
   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,CVSid6);
-  pout("%s",out);
   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);
@@ -140,99 +120,103 @@ const char opts[] = {
   SMARTAUTOSAVEENABLE,SMARTAUTOSAVEDISABLE,PRINTCOPYLEFT,SMART009MINUTES,QUIETMODE,VERYQUIETMODE,'h','?','\0'
 };
 
+unsigned char printcopyleft=0;
+
 /*      Takes command options and sets features to be run */	
 void ParseOpts (int argc, char** argv){
   int optchar;
   extern char *optarg;
   extern int optopt, optind, opterr;
   
+  memset(con,0,sizeof(*con));
+  con->testcase=-1;
   opterr=optopt=0;
   while (-1 != (optchar = getopt(argc, argv, opts))) {
     switch (optchar){
     case QUIETMODE:
-      quietmode=TRUE;
+      con->quietmode=TRUE;
       break;
     case VERYQUIETMODE:
-      veryquietmode=TRUE;
+      con->veryquietmode=TRUE;
       break;
     case SMART009MINUTES:
-      smart009minutes=TRUE;
+      con->smart009minutes=TRUE;
       break;
     case PRINTCOPYLEFT :
       printcopyleft=TRUE;
       break;
     case DRIVEINFO :
-      driveinfo  = TRUE;
+      con->driveinfo  = TRUE;
       break;		
     case CHECKSMART :
-      checksmart = TRUE;		
+      con->checksmart = TRUE;		
       break;
     case SMARTVERBOSEALL :
-      driveinfo = TRUE;
-      checksmart = TRUE;
-      generalsmartvalues = TRUE;
-      smartvendorattrib = TRUE;
-      smarterrorlog = TRUE;
-      smartselftestlog = TRUE;
+      con->driveinfo = TRUE;
+      con->checksmart = TRUE;
+      con->generalsmartvalues = TRUE;
+      con->smartvendorattrib = TRUE;
+      con->smarterrorlog = TRUE;
+      con->smartselftestlog = TRUE;
       break;
     case SMARTVENDORATTRIB :
-      smartvendorattrib = TRUE;
+      con->smartvendorattrib = TRUE;
       break;
     case GENERALSMARTVALUES :
-      generalsmartvalues = TRUE;
+      con->generalsmartvalues = TRUE;
       break;
     case SMARTERRORLOG :
-      smarterrorlog = TRUE;
+      con->smarterrorlog = TRUE;
       break;
     case SMARTSELFTESTLOG :
-      smartselftestlog = TRUE;
+      con->smartselftestlog = TRUE;
       break;
     case SMARTDISABLE :
-      smartdisable = TRUE;
+      con->smartdisable = TRUE;
       break;
     case SMARTENABLE :
-      smartenable   = TRUE;
+      con->smartenable   = TRUE;
       break;
     case SMARTAUTOSAVEENABLE:
-      smartautosaveenable = TRUE;
+      con->smartautosaveenable = TRUE;
       break;
     case SMARTAUTOSAVEDISABLE:
-      smartautosavedisable = TRUE;
+      con->smartautosavedisable = TRUE;
       break;
     case SMARTAUTOOFFLINEENABLE: 
-      smartautoofflineenable = TRUE;
+      con->smartautoofflineenable = TRUE;
       break;
     case SMARTAUTOOFFLINEDISABLE:
-      smartautoofflinedisable = TRUE;
+      con->smartautoofflinedisable = TRUE;
       break;
     case SMARTEXEOFFIMMEDIATE:
-      smartexeoffimmediate = TRUE;
-      testcase=OFFLINE_FULL_SCAN;
+      con->smartexeoffimmediate = TRUE;
+      con->testcase=OFFLINE_FULL_SCAN;
       break;
     case SMARTSHORTSELFTEST :
-      smartshortselftest = TRUE;
-      testcase=SHORT_SELF_TEST;
+      con->smartshortselftest = TRUE;
+      con->testcase=SHORT_SELF_TEST;
       break;
     case SMARTEXTENDSELFTEST :
-      smartextendselftest = TRUE;
-      testcase=EXTEND_SELF_TEST;
+      con->smartextendselftest = TRUE;
+      con->testcase=EXTEND_SELF_TEST;
       break;
     case SMARTSHORTCAPSELFTEST:
-      smartshortcapselftest = TRUE;
-      testcase=SHORT_CAPTIVE_SELF_TEST;
+      con->smartshortcapselftest = TRUE;
+      con->testcase=SHORT_CAPTIVE_SELF_TEST;
       break;
     case SMARTEXTENDCAPSELFTEST:
-      smartextendcapselftest = TRUE;
-      testcase=EXTEND_CAPTIVE_SELF_TEST;
+      con->smartextendcapselftest = TRUE;
+      con->testcase=EXTEND_CAPTIVE_SELF_TEST;
       break;
     case SMARTSELFTESTABORT:
-      smartselftestabort = TRUE;
-      testcase=ABORT_SELF_TEST;
+      con->smartselftestabort = TRUE;
+      con->testcase=ABORT_SELF_TEST;
       break;
     case 'h':
     case '?':
     default:
-      veryquietmode=FALSE;
+      con->veryquietmode=FALSE;
       printslogan();
       if (optopt){
 	pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
@@ -244,13 +228,13 @@ void ParseOpts (int argc, char** argv){
     }
   }
   // Do this here, so results are independent of argument order	
-  if (quietmode)
-    veryquietmode=TRUE;
+  if (con->quietmode)
+    con->veryquietmode=TRUE;
   
   // error message if user has asked for more than one test
-  if (1<(smartexeoffimmediate+smartshortselftest+smartextendselftest+
-	 smartshortcapselftest+smartextendcapselftest+smartselftestabort)){
-    veryquietmode=FALSE;
+  if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+
+	 con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort)){
+    con->veryquietmode=FALSE;
     printslogan();
     Usage();
     printf ("\nERROR: smartctl can only run a single test (or abort) at a time.\n\n");
@@ -269,13 +253,13 @@ void ParseOpts (int argc, char** argv){
 }
 
 
-// Printing function (controlled by global veryquietmode)
+// Printing function (controlled by global con->veryquietmode)
 void pout(char *fmt, ...){
   va_list ap;
 
   // initialize variable argument list 
   va_start(ap,fmt);
-  if (veryquietmode){
+  if (con->veryquietmode){
     va_end(ap);
     return;
   }
@@ -291,7 +275,11 @@ void pout(char *fmt, ...){
 int main (int argc, char **argv){
   int fd,retval=0;
   char *device;
-  
+  atamainctrl control;
+
+  // define control block for external functions
+  con=&control;
+
   // Part input arguments
   ParseOpts(argc,argv);
     
diff --git a/sm5/smartd.8 b/sm5/smartd.8
index 6c0f06385..a6729f06e 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/26 20:53:08 $" "smartmontools-5.0"
+.TH SMARTD 8  "$Date: 2002/10/28 23:47:00 $" "smartmontools-5.0"
 .SH NAME
 smartd \- S.M.A.R.T. Daemon
 .SH SYNOPSIS
@@ -27,8 +27,10 @@ Technology (S.M.A.R.T.) system built into many ATA-3 and later ATA,
 IDE and SCSI-3 hard drives. The purpose of S.M.A.R.T. is to monitor
 the reliability of the hard drive and predict drive failures, and to
 carry out different types of drive self-tests.  This version of
-smartd is compatible with ATA/ATAPI-5 and earlier standards (see
-REFERENCES below)
+.B smartd
+is compatible with ATA/ATAPI-5 and earlier standards (see 
+.B REFERENCES
+below)
 
 .B smartd  
 will notify users of S.M.A.R.T. errors and changes of
@@ -38,6 +40,12 @@ and warnings normally appear in
 It can be configured at start-up
 using the file
 .B /etc/smartd.conf.
+Note that 
+.B smartd
+only reads the configuration file at start-up.  Changes to the
+configuration file only take effect after the
+.B smartd
+daemon is restarted.
 
 .PP
 .SH SYNTAX
@@ -45,8 +53,7 @@ using the file
 .B 
 smartd 
 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 '\-'.
+argument begins with a '\-' followed by a letter or letters.
 
 In the absense of the configuration file
 .B /etc/smartd.conf
@@ -55,14 +62,17 @@ the
 smartd
 daemon scans for all devices that support S.M.A.R.T., using
 "/dev/hd*" for IDE/ATA devices, and "/dev/sd*" for SCSI devices.  It
-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
-.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.]
+polls these devices every 30 minutes checking for all possible S.M.A.R.T. errors
+(corresponding to the '\-a' option in the configuration file; please see 
+.B CONFIGURATION FILE
+below). When 
+.B smartd
+scans for devices on startup, 
+.B a warning message may appear in /var/log/messages,
+about missing block-major-xx devices.  These messages are usually
+harmless. Alternatively, the configuration file can be used to exclude
+non-existent devices by giving a list of devices to monitor at start
+up.
 .P
 .SH 
 OPTIONS
@@ -73,8 +83,9 @@ STDOUT and then exits. Please include this information if you are
 reporting bugs.
 .TP
 .B X
-eXamine: Runs smartd in "debug" mode. In this mode, it does not fork and
-displays status information to STDOUT.
+eXamine: Runs smartd in "debug" mode. In this mode, it does not fork
+and displays status information to STDOUT.  It also prints more
+verbose information about what it is doing.
 
 .SH EXAMPLES
 
@@ -82,70 +93,219 @@ displays status information to STDOUT.
 smartd
 .fi
 Runs the daemon in forked mode. This is the normal way to run
-.B
-smartd.
-.fi
+.B smartd.
 Entries are logged to
 .B /var/log/messages.
 
 .fi
-Note that smartmontools provides a start-up script in
+Note that 
+.B smartmontools
+provides a start-up script in
 .B /etc/rc.d/init.d/smartd
 which is responsible for starting and stopping the daemon via the
 normal init interface. 
 Using this script, you can start
 .B
 smartd
-by giving the command
-.B '/etc/rc.d/init.d/smartd start'
-and stop it by using
-.B '/etc/rc.d/init.d/smartd stop'.
+by giving the command:
+.nf
+.B /etc/rc.d/init.d/smartd start
+.fi
+and stop it by using the command:
+.nf
+.B /etc/rc.d/init.d/smartd stop
 
+.fi
 If you want
 .B smartd
 to start running whenever your machine is booted, this can be enabled
 by using the command:
 .nf
-.B '/sbin/chkconfig --add smartd'
+.B /sbin/chkconfig --add smartd
 .fi
 and disabled using the command:
 .nf
-.B '/sbin/chkconfig --del smartd'
+.B /sbin/chkconfig --del smartd
 
-.SH CONFIGURATION FILE
+.SH CONFIGURATION FILE /etc/smartd.conf
 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:
+will try to open the 12 ATA devices 
+.B /dev/hd[a-l] 
+and the 26 SCSI
+devices 
+.B /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.
+
+One can avoid this problem, and gain more control over what types of
+changes and events
+.B smartd
+is watching for, by using the configuration file
+.B /etc/smartd.conf.
+This file contains a list of devices to monitor, with one device per
+line.  An example file is included with the
+.B smartmontools
+distribution, but is not normally installed in /etc.  If
+.B smartmontools
+was properly installed on your system, you will find this sample
+configuration file in
+.B /usr/share/doc/smartmontools-5.0/.
+For security, the configuration file should not be writable by anyone
+but root. The syntax of the file is as follows:
+
+There should be one device listed per line, although you may have
+lines that are entirely comments or white space.
+
+Any text following a hash sign (#) and up to the end of the line is
+taken to be a comment, and ignored.
+
+Lines may be continued by using a backslash (\(rs) as the last
+non-whitespace or non-comment item on a line.
+
+Here is an example configuration file.  It's for illustrative purposes
+only; please don't copy it onto your system without reading to the end
+of the
+.B DIRECTIVES
+Section below!
 
 .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 # /etc/smartd.conf for monitoring three IDE disks
+.B # and two SCSI disks.
 
-.B # first IDE disk on each of two interfaces
-.B /dev/hda
-.B /dev/hdc
+.nf
+.B # First IDE disk on each of two interfaces
+.B /dev/hda -a  
+.B /dev/hdc -a -I 194 -I 5 -i 12
 
+.nf
 .B # SCSI disk
 .B /dev/sda
+
+.nf
+.B # Strange device.  It's SCSI.
+.B /dev/weird -S
+
+.nf
+.B # And here is a continued line
+.B /dev/hdd\ -L\ -l\ -t\ \(rs\ \ # Attributes below not tracked
+.B\ \ \ \ \ \ \ \ \ \ \ \ -I\ 194\ \(rs\ \ # temperature
+.B\ \ \ \ \ \ \ \ \ \ \ \ -I\ 231\ \(rs\ \ # also temperature
+.B\ \ \ \ \ \ \ \ \ \ \ \ -I 9\ \ \ \ \ \ # power-on hours
 .fi
 
-If
-.B smartmontools
-was properly installed on your system, you should be able to find a sample configuration file in
-.B /usr/share/doc/smartmontools-5.0
+
+.PP 
+.SH CONFIGURATION FILE DIRECTIVES
+.PP
+.sp 2
+The following are the directives that may appear following the device
+name on any line of the
+.B /etc/smartd.conf
+configuration file. Note that
+.B these are NOT command-line options for smartd.
+The command-line options for
+.B smartd
+are listed above.  The directives below may appear in any order,
+following the device name.  For the moment, apart from the '\-S'
+option, these directives only apply to ATA disks.  For ATA disks, if
+no directives appear, the disk will not be monitored.  The '\-a'
+directive will try to monitor everything possible.
+.TP
+.B A
+ATA: The device is an ATA device.  Don't try issuing SCSI commands to it.
+.TP
+.B S
+SCSI: The device is a SCSI device.  Don't try issuing IDE/ATA
+commands to it.  
+
+In the absence of either of these directives,
+.B smartd
+will attempt to guess the device type by looking at whether the fifth
+character in the device name is an 's' or an 'h'.  If it can't guess
+from this fifth character, then it will simply try to access the
+device using first ATA and then SCSI ioctl()s.
+.TP
+.B c
+Check: Will check the SMART status of the disk.  If any prefailure
+attributes are less than or equal to their threshold values, then disk
+failure is predicted in less than 24 hours, and a message at priority
+.B 'CRITICAL'
+will be logged to syslog.
+.TP
+.B l
+Log: Report if that the number of ATA errors reported in the ATA
+Error Log has increased since the last check.
+.TP
+.B L
+Log: Report if that the number of errors reported in the SMART
+Self-Test Log has increased since the last check.  Note that such
+errors will
+.I only 
+be logged if you run self-tests on the disk (and it fails the tests!).
+Self-tests can be run by using the '\-SXsxO' options of
+.B smartctl.
+Please see the 
+.B smartctl
+man pages.
+.TP
+.B f
+Fail: Check for 'failure' of any usage attributes.  If these
+attributes are less than or equal to the threshold, it does NOT
+indicate imminent disk failure.  It "indicates an advisory condition
+where the usage or age of the device has exceeded its intended design
+life period."
+.TP
+.B p
+Prefail: Report anytime that a prefail attribute has changed
+its value since the last check, 30 min ago.
+.TP
+.B u
+Usage: Report anytime that a usage attribute has changed its value
+since the last check, 30 min ago.
+.TP
+.B t
+Track: Equivalent to turning on the two previous flags '\-t' and '\-u'.
+Tracks changes in
+.I all
+device attributes.
+.TP
+.B i INT
+Ignore: This directive modifies the behavior of the '\-f' attribute
+and has no effect without it.  
+.I This directive takes a decimal integer argument INT in the range from 1 to 255.
+It means to ignore device attribute number INT, when checking for
+failure of usage attributes.  This is useful, for example, if you have
+a very old disk and don't want to keep getting messages about the
+hours-on-lifetime attribute (usually attribute 9) failing.
+.TP
+.B I INT
+Ignore: This directive modifies the
+behavior of the '\-p', '\-u', and '\-t' attributes
+and has no effect without one of them.  
+.I This directive takes a decimal integer argument INT in the range from 1 to 255.
+It means to ignore device attribute INT, when tracking changes in the
+attribute values.  This is useful, for example, if one of the device
+attributes is the disk temperature (usually attribute 194 or
+231). It's annoying to get reports each time the temperature changes.
+.TP
+.B a
+All: equivalent to turning on the following directives: 
+.B '\-c' 
+to check the SMART status,
+.B '\-f' 
+to report failures of usage (rather than pre-fail) attributes,
+.B '\-t' 
+to track changes in both prefailure and usage attributes,
+.B '\-L' 
+to report increases in the number of self-test log errors, and
+.B '\-l' 
+to report increases in the number of ATA errors.
+
 
 .SH NOTES
 .B smartd
@@ -204,7 +364,8 @@ REFERENCES FOR S.M.A.R.T.
 If you would like to understand better how S.M.A.R.T. works, and what
 it does, a good place to start is  Section 8.41 of the 'AT
 Attachment with Packet Interface-5' (ATA/ATAPI-5) specification.  This
-documents the S.M.A.R.T. functionality which the smartmontools
+documents the S.M.A.R.T. functionality which the 
+.B smartmontools
 utilities provide access to.  You can find Revision 1 of this document
 at:
 .nf
@@ -242,4 +403,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.13 2002/10/26 20:53:08 ballen4705 Exp $
+$Id: smartd.8,v 1.14 2002/10/28 23:47:00 ballen4705 Exp $
diff --git a/sm5/smartd.c b/sm5/smartd.c
index 5bb4c54c4..f3a86e062 100644
--- a/sm5/smartd.c
+++ b/sm5/smartd.c
@@ -20,8 +20,7 @@
  *
  */
 
-#include <errno.h>
-#include <stdlib.h>
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
@@ -32,15 +31,23 @@
 #include <linux/hdreg.h>
 #include <syslog.h>
 #include <stdarg.h>
-
+#include <signal.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "smartd.h"
+#include "ataprint.h"
+#include "extern.h"
 
 // CVS ID strings
 extern const char *CVSid1, *CVSid2;
-const char *CVSid3="$Id: smartd.c,v 1.36 2002/10/26 19:33:40 ballen4705 Exp $" 
-CVSID1 CVSID4 CVSID7;
+const char *CVSid6="$Id: smartd.c,v 1.37 2002/10/28 23:47:00 ballen4705 Exp $" 
+CVSID1 CVSID2 CVSID3 CVSID4 CVSID7;
+
+// global variable used for control of printing, passing arguments, etc.
+atamainctrl *con=NULL;
 
 // This function prints either to stdout or to the syslog as needed
 void printout(int priority,char *fmt, ...){
@@ -56,20 +63,33 @@ void printout(int priority,char *fmt, ...){
   return;
 }
 
-// Printing function for debugging atacmds. For debugging set 0 to 1
+// Printing function for debugging atacmds.
 // in #if statement
 void pout(char *fmt, ...){
   va_list ap;
-  
   // initialize variable argument list 
   va_start(ap,fmt);
-#if (0)
-  vprintf(fmt,ap);
-#endif
+  // in debug mode we will print the output from the ataprint.o functions!
+  if (debugmode)
+    vprintf(fmt,ap);
   va_end(ap);
   return;
 }
 
+void goobye(){
+  printout(LOG_CRIT,"smartd is exiting\n");
+  return;
+}
+
+volatile sig_atomic_t fatal = 0;
+// simple signal handler to print goodby message to syslog
+void sighandler(int sig){
+    printout(LOG_CRIT,"smartd received signal %d: %s\n",
+	     sig,strsignal(sig));
+    exit(1);
+}
+
+
 // Forks new process, closes all file descriptors, redirects stdin,
 // stdout, stderr
 int daemon_init(void){
@@ -78,16 +98,29 @@ int daemon_init(void){
 
   if ((pid=fork()) < 0) {
     // unable to fork!
-    printout(LOG_CRIT,"Unable to fork daemon process!\n");
+    printout(LOG_CRIT,"smartd unable to fork daemon process!\n");
     exit(1);
   }
   else if (pid)
     // we are the parent process -- exit cleanly
     exit(0);
   
-  // from here on, we are the child process
+  // from here on, we are the child process.
   setsid();
 
+
+  // Fork one more time to avoid any possibility of having terminals
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    printout(LOG_CRIT,"smartd unable to fork daemon process!\n");
+    exit(1);
+  }
+  else if (pid)
+    // we are the parent process -- exit cleanly
+    exit(0);
+
+  // Now we are the child's child...
+
   // close any open file descriptors
   for (i=getdtablesize();i>=0;--i)
     close(i);
@@ -111,13 +144,37 @@ void printhead(){
   return;
 }
 
+
+// prints help info for configuration file directives
+void Directives() {
+  printout(LOG_INFO,"Configuration file Directives (following device name):\n");
+  printout(LOG_INFO,"  -A    Device is an ATA device\n");
+  printout(LOG_INFO,"  -S    Device is a SCSI device\n");
+  printout(LOG_INFO,"  -c    Monitor SMART Health Status\n");
+  printout(LOG_INFO,"  -l    Monitor SMART Error Log for changes\n");
+  printout(LOG_INFO,"  -L    Monitor SMART Self-Test Log for new errors\n");
+  printout(LOG_INFO,"  -f    Monitor for failure of any 'Usage' Attributes\n");
+  printout(LOG_INFO,"  -p    Report changes in 'Prefailure' Attributes\n");
+  printout(LOG_INFO,"  -u    Report changes in 'Usage' Attributes\n");
+  printout(LOG_INFO,"  -t    Equivalent to -p and -u Directives\n");
+  printout(LOG_INFO,"  -i ID Ignore Attribute ID for -f Directive\n");
+  printout(LOG_INFO,"  -I ID Ignore Attribute ID for -p, -u or -t Directive\n");
+  printout(LOG_INFO,"   #    Comment: text after a hash sign is ignored\n");
+  printout(LOG_INFO,"   \\    Line continuation character\n");
+  printout(LOG_INFO,"Attribute ID is a decimal integer 1 <= ID <= 255\n");
+  printout(LOG_INFO,"All but -S directive are only implemented for ATA devices\n");
+  printout(LOG_INFO,"Example: /dev/hda -a\n");
+return;
+}
+
 /* prints help information for command syntax */
 void Usage (void){
   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\n",PRINTCOPYLEFT);
+  printout(LOG_INFO,"Command Line Options:\n");
+  printout(LOG_INFO,"  %c  Start smartd in debug Mode\n",DEBUGMODE);
+  printout(LOG_INFO,"  %c  Print License, Copyright, and version information\n\n",PRINTCOPYLEFT);
   printout(LOG_INFO,"Optional configuration file: %s\n",CONFIGFILE);
+  Directives();
 }
 
 // returns negative if problem, else fd>=0
@@ -125,9 +182,9 @@ int opendevice(char *device){
   int 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);
+      printout(LOG_INFO,"Device: %s, %s, open() failed\n",device, sys_errlist[errno]);
     else
-      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+      printout(LOG_INFO,"Device: %s, open() failed\n",device);
     return -1;
   }
   // device opened sucessfully
@@ -135,80 +192,150 @@ int opendevice(char *device){
 }
 
 // returns 1 if problem, else zero
-int closedevice(int fd){
+int closedevice(int fd, char *name){
   if (close(fd)){
     if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Closing file descriptor %d failed\n",sys_errlist[errno],fd);
+      printout(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, sys_errlist[errno], fd);
     else
-      printout(LOG_INFO,"Closing file descriptor %d failed\n",fd);
+      printout(LOG_INFO,"Device: %s, close(%d) failed\n",name,fd);
     return 1;
   }
-  // device opened sucessfully
+  // device sucessfully closed
   return 0;
 }
 
+// returns <0 on failure
+int ataerrorcount(int fd, char *name){
+  struct ata_smart_errorlog log;
+  
+  if (-1==ataReadErrorLog(fd,&log)){
+    printout(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of ATA errors
+  return log.error_log_pointer?log.ata_error_count:0;
+}
+
+// returns <0 if problem
+char selftesterrorcount(int fd, char *name){
+  struct ata_smart_selftestlog log;
+
+  if (-1==ataReadSelfTestLog(fd,&log)){
+    printout(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of self-test errors
+  return (char)ataPrintSmartSelfTestlog(log,0);
+}
+
+
+
 // scan to see what ata devices there are, and if they support SMART
-int atadevicescan (atadevices_t *devices, char *device){
+int atadevicescan2(atadevices_t *devices, cfgfile *cfg){
   int fd;
   struct hd_driveid drive;
+  char *device=cfg->name;
   
-  printout(LOG_INFO,"Opening device %s\n", device);
+  // should we try to register this as an ATA device?
+  if (!(cfg->tryata))
+    return 1;
+  
+  // open the device
   if ((fd=opendevice(device))<0)
     // device open failed
     return 1;
+  printout(LOG_INFO,"Device: %s, opened\n", device);
   
+  // Get drive identity structure
   if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
     // device exists, but not able to do SMART
+    printout(LOG_INFO,"Device: %s, not SMART capable, or couldn't enable SMART\n",device);
     close(fd);
-    printout(LOG_INFO,"Device: %s, Found but not SMART capable, or couldn't enable SMART\n",device);
-    return 2;
+    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;
+  // capability check: SMART status
+  if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
+    printout(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",device);
+    cfg->smartcheck=0;
   }
-  else if (ataReadSmartThresholds (fd,&devices[numatadevices].smartthres)){
+  
+  // capability check: Read smart values and thresholds
+  if (cfg->usagefailed || cfg->prefail || cfg->usage) {
+    devices->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values));
+    devices->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds));
+    
+    if (!devices->smartval || !devices->smartthres){
+      printout(LOG_CRIT,"Not enough memory to obtain SMART data\n");
+      exit(1);
+    }
+    
+    if (ataReadSmartValues(fd,devices->smartval) ||
+	ataReadSmartThresholds (fd,devices->smartthres)){
+      printout(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",device);
+      free(devices->smartval);
+      free(devices->smartthres);
+      cfg->usagefailed=cfg->prefail=cfg->usage=0;
+    }
+  }
+  
+  // capability check: self-test-log
+  if (cfg->selftest){
+    char val=selftesterrorcount(fd, device);
+    if (val>=0)
+      cfg->selflogcount=val;
+    else
+      cfg->selftest=0;
+  }
+  
+  // capability check: ATA error log
+  if (cfg->errorlog){
+    int val=ataerrorcount(fd, device);
+    if (val>=0)
+      cfg->ataerrorcount=val;
+    else
+      cfg->errorlog=0;
+  }
+  
+  // If not tests available or selected, return
+  if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || 
+	cfg->usagefailed || cfg->prefail || cfg->usage)) {
     close(fd);
-    printout(LOG_INFO,"Device: %s, Read SMART Thresholds Failed\n",device);
-    return 4;
+    return 3;
   }
   
-  // Device exists, and does SMART.  Add to list
+  // Do we still have entries available?
   if (numatadevices>=MAXATADEVICES){
     printout(LOG_CRIT,"smartd has found more than MAXATADEVICES=%d ATA devices.\n"
 	     "Recompile code from " PROJECTHOME " with larger MAXATADEVICES\n",numatadevices);
     exit(1);
   }
-
-  printout(LOG_INFO,"%s Found and is SMART capable. Adding to \"monitor\" list.\n",device);
-  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);
+  printout(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",device);
+  // no need to try sending SCSI commands to this device!
+  cfg->tryscsi=0;
+  
+  // we were called from a routine that has global storage for the name.  Keep pointer.
+  devices->devicename=device;
+  devices->cfg=cfg;
+  
   
   numatadevices++;
-  closedevice(fd);
+  closedevice(fd, device);
   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??
-int scsidevicescan (scsidevices_t *devices, char *device){
+// object?? Can anyone explain this obscurity?
+int scsidevicescan(scsidevices_t *devices, char *device){
   int i, fd, smartsupport;
   unsigned char  tBuf[4096];
   
-  printout(LOG_INFO,"Opening device %s\n", device);
+  printout(LOG_INFO,"Device: %s, opening\n", device);
   if ((fd=opendevice(device))<0)
     // device open failed
     return 1;
@@ -243,8 +370,10 @@ int scsidevicescan (scsidevices_t *devices, char *device){
   }
 
   // now we can proceed to register the device
-  printout(LOG_INFO, "Device: %s, Found and is SMART capable. Adding to \"monitor\" list.\n",device);
-  strcpy(devices[numscsidevices].devicename,device);
+  printout(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n",device);
+
+  // since device points to global memory, just keep that address
+  devices[numscsidevices].devicename=device;
 
   // register the supported functionality.  The smartd code does not
   // seem to make any further use of this information.
@@ -263,83 +392,195 @@ int scsidevicescan (scsidevices_t *devices, char *device){
     }	
   }
   numscsidevices++;
-  closedevice(fd);
+  closedevice(fd, device);
   return 0;
 }
 
+// We compare old and new values of the n'th attribute.  Note that n
+// is NOT the attribute ID number.. If equal, return 0.  The thre
+// structure is used to verify that the attributes are valid ones.  If
+// the new value is lower than the old value, then we return both old
+// and new values. new value=>lowest byte, old value=>next-to-lowest
+// byte, id value=>next-to-next-to-lowest byte., and prefail flag x as
+// bottom bit of highest byte.  See below (lsb on right)
 
-void ataCompareSmartValues (atadevices_t *device, struct ata_smart_values new ){
-    int i;
-    int oldval,newval,idold,idnew;
-    
-    for ( i =0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++){
-      // which device is it?
-      idnew=new.vendor_attributes[i].id;
-      idold=device->smartval.vendor_attributes[i].id;
-      
-      if (idold && idnew){
-	// if it's a valid attribute, compare values
-	newval=new.vendor_attributes[i].current;
-	oldval=device->smartval.vendor_attributes[i].current;
-	if (oldval!=newval){
-	  // values have changed; print them
-	  char *loc,attributename[64];
-	  loc=attributename;
-	  ataPrintSmartAttribName(attributename,idnew);
-	  // skip blank space in name
-	  while (*loc && *loc==' ')
-	    loc++;
-	  printout(LOG_INFO, "Device: %s, SMART Attribute: %s changed from %i to %i\n",
-		   device->devicename,loc,oldval,newval);
-	}
-      }
-    }
+//  [00000000x][attribute ID][old value][new value]
+int  ataCompareSmartValues2(struct ata_smart_values *new,
+			    struct ata_smart_values *old,
+			    struct ata_smart_thresholds *thresholds,
+			    int n){
+  struct ata_smart_attribute *now,*was;
+  struct ata_smart_threshold_entry *thre;
+  unsigned char oldval,newval;
+  int returnvalue;
+
+  // check that attribute number in range, and no null pointers
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thre)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  now=new->vendor_attributes+n;
+  was=old->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  // consider only valid attributes, with same valid ID in all structures
+  if (!now->id || !was->id || !thre->id || (now->id != was->id) || (now->id != thre->id))
+    return 0;
+
+  // if values have not changed, return
+  newval=now->current;
+  oldval=was->current;
+
+  // if any values out of the allowed range, or the values haven't changed, return
+  if (!newval || !oldval || newval>0xfe || oldval>0xfe || oldval==newval)
+    return 0;
+  
+  // values have changed.  Construct output
+  returnvalue=0;
+  returnvalue |= newval;
+  returnvalue |= oldval<<8;
+  returnvalue |= now->id<<16;
+  returnvalue |= (now->status.flag.prefailure)<<24;
+
+  return returnvalue;
 }
 
+// This looks to see if the corresponding bit of the 32 bytes is set.
+// This wastes a few bytes of storage but eliminates all searching and
+// sorting functions! Entry is ZERO <==> the attribute ON. Calling
+// with set=0 tells you if the attribute is being tracked or not.
+// Calling with set=1 turns the attribute OFF.
+int isattoff(unsigned char attr,unsigned char *data, int set){
+  // locate correct attribute
+  int loc=attr>>3;
+  int bit=attr & 0x07;
+  unsigned char mask=0x01<<bit;
 
-int ataCheckDevice( atadevices_t *drive){
-  struct ata_smart_values tempsmartval;
-  struct ata_smart_thresholds tempsmartthres;
-  int failed,fd;
-  char *loc,attributename[64];
+  // attribute zero is always OFF
+  if (!attr)
+    return 1;
 
+  if (!set)
+    return (data[loc] & mask);
+  
+  data[loc]|=mask;
+  // return value when setting makes no sense!
+  return 0;
+}
+
+
+int ataCheckDevice(atadevices_t *drive){
+  int fd,i;
+  char *name=drive->devicename;
+  cfgfile *cfg=drive->cfg;
+  
   // if we can't open device, fail gracefully rather than hard --
   // perhaps the next time around we'll be able to open it
-  if ((fd=opendevice(drive->devicename))<0)
+  if ((fd=opendevice(name))<0)
     return 1;
   
-  // Coming into this function, *drive contains the last values measured,
-  // and we read the NEW values into tempsmartval
-  if (ataReadSmartValues(fd,&tempsmartval))
-    printout(LOG_INFO, "%s:Failed to read SMART values\n", drive->devicename);
-  
-  // and we read the new thresholds into tempsmartthres
-  if (ataReadSmartThresholds(fd, &tempsmartthres))
-    printout(LOG_INFO, "%s:Failed to read SMART thresholds\n",drive->devicename);
-  
-  // See if any vendor attributes are below minimum, and print them
-  // out.  WHEN IT WORKS, we should here add a call to
-  // ataSmartStatus2() either in addition to or instead of the
-  // ataCheckSmart command below. This is the "right" long-term
-  // solution.
-  if ((failed=ataCheckSmart(tempsmartval,tempsmartthres,1))){
-    ataPrintSmartAttribName(attributename,failed);
-    // skip blank space in name
-    loc=attributename;
-    while (*loc && *loc==' ')
-      loc++;
-    printout(LOG_CRIT,"Device: %s, Failed SMART attribute: %s. Use smartctl -a %s.\n",
-	     drive->devicename,loc,drive->devicename);
-  }
-  
-  // see if any values have changed.  Second argument is new values
-  ataCompareSmartValues(drive, tempsmartval);
-  
-  // Save the new values into *drive for the next time around
-  drive->smartval = tempsmartval;
-  drive->smartthres = tempsmartthres;
-  
-  closedevice(fd);
+  // check smart status
+  if (cfg->smartcheck){
+    int status=ataSmartStatus2(fd);
+    if (status==-1)
+      printout(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
+    else if (status==1)
+      printout(LOG_INFO,"Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n",name);
+  }
+  
+  // Check everything that depends upon SMART Data (eg, Attribute values)
+  if (cfg->usagefailed || cfg->prefail || cfg->usage){
+    struct ata_smart_values     curval;
+    struct ata_smart_thresholds *thresh=drive->smartthres;
+    
+    // Read current attribute values. *drive contains old values adn thresholds
+    if (ataReadSmartValues(fd,&curval))
+      printout(LOG_INFO, "Device: %s, failed to read SMART Attribute Data\n", name);
+    else {  
+      // look for failed usage attributes, or track usage or prefail attributes
+      for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+	int att;
+	
+	// This block looks for usage attributes that have failed.
+	// Prefail attributes that have failed are returned with a
+	// positive sign. No failure returns 0. Usage attributes<0.
+	if (cfg->usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){
+	  
+	  // are we tracking this attribute?
+	  att *= -1;
+	  if (!isattoff(att, cfg->failatt, 0)){
+	    char attname[64], *loc=attname;
+	    
+	    // get attribute name & skip white space
+	    ataPrintSmartAttribName(loc, att);
+	    while (*loc && *loc==' ') loc++;
+	    
+	    // warning message
+	    printout(LOG_CRIT,"Device: %s, Failed SMART usage attribute: %s. Use smartctl -v %s.\n", name, loc, name);
+	  }
+	}
+	
+	// This block tracks usage or prefailure attributes to see if they are changing
+	if ((cfg->usage || cfg->prefail) && ((att=ataCompareSmartValues2(&curval, drive->smartval, thresh, i)))){
+	  const int mask=0xff;
+	  int prefail=(att>>24) & mask;
+	  int id     =(att>>16) & mask;
+	  int oldval =(att>>8)  & mask;
+	  int newval =(att>>0)  & mask;
+	  char attname[64],*loc=attname;
+	  
+	  // are we tracking this attribute?
+	  if (!isattoff(id, cfg->trackatt, 0)){
+	    
+	    // get attribute name, skip spaces
+	    ataPrintSmartAttribName(loc, id);
+	    while (*loc && *loc==' ') loc++;
+	    
+	    // prefailure attribute
+	    if (cfg->prefail && prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %i to %i\n",
+		       name, loc, oldval, newval);
+
+	    // usage attribute
+	    if (cfg->usage && !prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %i to %i\n",
+		       name, loc, oldval, newval);
+	  }
+	} // endof block tracking usage or prefailure
+      } // end of loop over attributes
+     
+      // Save the new values into *drive for the next time around
+      memcpy(drive->smartval,&curval,sizeof(curval));
+    } 
+  }
+  
+  // check if number of selftest errors has increased (note: may also DECREASE)
+  if (cfg->selftest){
+    char old=cfg->selflogcount;
+    char new=selftesterrorcount(fd, name);
+    if (new>old){
+      printout(LOG_CRIT,"Device: %s, Self-Test Log error count increased from %d to %d\n",
+	       name,old,new);
+    }
+    if (new>=0)
+      // Needed suince self-test error count may  DECREASE
+      cfg->selflogcount=new;
+  }
+
+  
+  // check if number of ATA errors has increased
+  if (cfg->errorlog){
+    int old=cfg->ataerrorcount;
+    int new=ataerrorcount(fd, name);
+    if (new>old){
+      printout(LOG_CRIT,"Device: %s, ATA error count increased from %d to %d\n",
+	       name,old,new);
+    }
+    // this last line is probably not needed, count always increases
+    if (new>=0)
+      cfg->ataerrorcount=new;
+  }
+  closedevice(fd, name);
   return 0;
 }
 
@@ -359,7 +600,7 @@ int scsiCheckDevice( scsidevices_t *drive){
   currenttemp = triptemp = 0;
   
   if (scsiCheckSmart(fd, drive->SmartPageSupported, &returnvalue, &currenttemp, &triptemp))
-    printout(LOG_INFO, "%s:Failed to read SMART values\n", drive->devicename);
+    printout(LOG_INFO, "Device: %s, failed to read SMART values\n", drive->devicename);
   
   if (returnvalue)
     printout(LOG_CRIT, "Device: %s, SMART Failure: (%02x) %s\n", drive->devicename, 
@@ -375,11 +616,11 @@ int scsiCheckDevice( scsidevices_t *drive){
 	       drive->devicename, (int) (currenttemp - drive->Temperature), (unsigned int) currenttemp );
     drive->Temperature = currenttemp;
   }
-  closedevice(fd);
+  closedevice(fd, drive->devicename);
   return 0;
 }
 
-void CheckDevices (  atadevices_t *atadevices, scsidevices_t *scsidevices){
+void CheckDevices(atadevices_t *atadevices, scsidevices_t *scsidevices){
   int i;
   
   // If there are no devices to monitor, then exit
@@ -410,12 +651,197 @@ char copyleftstring[]=
 cfgfile config[MAXENTRIES];
 
 
-// returns number of entries in config file, or 0 if no config file exists
+int parsetoken(char *token,cfgfile *cfg){
+  char sym=token[1];
+  char *name=cfg->name;
+  int lineno=cfg->lineno;
+  char *delim=" \n\t";
+
+  // is the rest of the line a comment
+  if (*token=='#')
+    return 1;
+  
+  // is the token not recognized?
+  if (*token!='-' || strlen(token)!=2) {
+    printout(LOG_CRIT,"Drive: %s, unknown Directive: %s at line %d of file %s\n",
+	     name,token,lineno,CONFIGFILE);
+    Directives();
+    exit(1);
+  }
+  
+  // let's parse the token and swallow its argument
+  switch (sym) {
+    char *arg;
+    char *endptr;
+    int val;
+    
+  case 'A':
+    // ATA device
+    cfg->tryata=1;
+    cfg->tryscsi=0;
+    break;
+  case 'S':
+    //SCSI device
+    cfg->tryscsi=1;
+    cfg->tryata=0;
+    break;
+  case 'c':
+    // check SMART status
+    cfg->smartcheck=1;
+    break;
+  case 'f':
+    // check for failure of usage attributes
+    cfg->usagefailed=1;
+    break;
+  case 't':
+    // track changes in all vendor attributes
+    cfg->prefail=1;
+    cfg->usage=1;
+    break;
+  case 'p':
+    // track changes in prefail vendor attributes
+    cfg->prefail=1;
+    break;
+  case 'u':
+    //  track changes in usage vendor attributes
+    cfg->usage=1;
+    break;
+  case 'L':
+    // track changes in self-test log
+    cfg->selftest=1;
+    break;
+  case 'l':
+    // track changes ATA error log
+    cfg->errorlog=1;
+    break;
+  case 'a':
+    // monitor everything
+    cfg->smartcheck=1;
+    cfg->prefail=1;
+    cfg->usagefailed=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+    break;
+  case 'i':
+  case 'I':
+    // ignore a particular vendor attribute for tracking (i) or
+    // failure (I)
+    arg=strtok(NULL,delim);
+    // make sure argument is there
+    if (!arg) {
+      printout(LOG_CRIT,"Drive %s Directive: %s at line %d of file %s needs integer argument.\n",
+	       name,token,lineno,CONFIGFILE);
+      Directives();
+      exit(1);
+    }
+    // get argument value, check that it's properly-formed, an
+    // integer, and in-range
+    val=strtol(arg,&endptr,10);
+    if (*endptr!='\0' || val<=0 || val>255)  {
+      printout(LOG_CRIT,"Drive %s Directive: %s at line %d of file %s has argument: %s, needs 0 < n < 256\n",
+	       name,token,lineno,CONFIGFILE,arg);
+      Directives();
+      exit(1);
+    }
+    // put into correct list (bitmaps, access only with isattoff()
+    // function. Turns OFF corresponding attribute.
+    if (sym=='I')
+      isattoff(val,cfg->trackatt,1);
+    else
+      isattoff(val,cfg->failatt,1);
+    break;
+  default:
+    printout(LOG_CRIT,"Drive: %s, unknown option: %s at line %d of file %s\n",
+	     name,token,lineno,CONFIGFILE);
+    Directives();
+    exit(1);
+  }
+  return 1;
+}
+
+
+int parseconfigline(int entry, int lineno,char *line){
+  char *token,*copy;
+  char *delim=" \n\t";
+  char *name;
+  int len;
+  cfgfile *cfg;
+  
+  if (!(copy=strdup(line))){
+    perror("no memory available to parse line");
+    exit(1);
+  }
+  
+  // get first token -- device name
+  if (!(name=strtok(copy,delim)) || *name=='#'){
+    free(copy);
+    return 0;
+  }
+
+  // Is there space for another entry?
+  if (entry>=MAXENTRIES){
+    printout(LOG_CRIT,"Error: configuration file %s can have no more than %d entries\n",
+	     CONFIGFILE,MAXENTRIES);
+    exit(1);
+  }
+
+  // We've got a legit entry, clear structure
+  cfg=config+entry;
+  memset(cfg,0,sizeof(*config));
+
+  // Save info to process memory for after forking 32 bytes contains 1
+  // bit per possible attribute ID.  See isattoff()
+  cfg->name=strdup(name);
+  cfg->failatt=(unsigned char *)calloc(32,1);
+  cfg->trackatt=(unsigned char *)calloc(32,1);
+  
+  if (!cfg->name || !cfg->failatt || !cfg->trackatt) {
+    perror("no memory available to save name");
+    exit(1);
+  }
+
+  cfg->lineno=lineno;
+  cfg->tryscsi=cfg->tryata=1;
+  
+  // Try and recognize if a IDE or SCSI device.  These can be
+  // overwritten by configuration file directives.
+  len=strlen(name);
+  if (len>5 && !strncmp("/dev/h",name, 6))
+    cfg->tryscsi=0;
+  
+  if (len>5 && !strncmp("/dev/s",name, 6))
+    cfg->tryata=0;
+
+  // parse tokens one at a time from the file
+  while ((token=strtok(NULL,delim)) && parsetoken(token,cfg)){
+#if 0
+  printout(LOG_INFO,"Parsed token %s\n",token);
+#endif
+  }
+
+  // basic sanity check -- are any options turned on?
+  if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail || cfg->usage || cfg->selftest || cfg->errorlog || cfg->tryscsi)){
+    printout(LOG_CRIT,"Drive: %s, no monitoring Directives on line %d of file %s\n",
+	     cfg->name, cfg->lineno, CONFIGFILE);
+    Directives();
+    exit(1);
+  }
+
+  entry++;
+  free(copy);
+  return 1;
+}
+
+// returns number of entries in config file, or 0 if no config file
+// exists.  A config file with zero entries will cause an error
+// message and an exit.
 int parseconfigfile(){
   FILE *fp;
-  int entry=0,lineno=0;
+  int entry=0,lineno=1,cont=0,contlineno=0;
   char line[MAXLINELEN+2];
-  
+  char fullline[MAXCONTLINE+1];
+
   // Open config file, if it exists
   fp=fopen(CONFIGFILE,"r");
   if (fp==NULL && errno!=ENOENT){
@@ -432,14 +858,34 @@ int parseconfigfile(){
   if (fp==NULL)
     return 0;
   
-  // configuration file exists.  Read it and search for devices
+  // configuration file exists
   printout(LOG_INFO,"Using configuration file %s\n",CONFIGFILE);
-  while (fgets(line,MAXLINELEN+2,fp)){
-    int len;
-    char *dev;
+
+  // parse config file line by line
+  while (1) {
+    int len=0;
+    char *lastslash;
+    char *comment;
+    char *code;
+
+    // make debugging simpler
+    memset(line,0,sizeof(line));
+
+    // get a line
+    code=fgets(line,MAXLINELEN+2,fp);
     
-    // track linenumber for error messages
-    lineno++;
+    // are we at the end of the file?
+    if (!code){
+      if (cont) {
+	// the final line is part of a continuation line
+	cont=0;
+	entry+=parseconfigline(entry,lineno,fullline);
+      }
+      break;
+    }
+
+    // input file line number
+    contlineno++;
     
     // See if line is too long
     len=strlen(line);
@@ -449,77 +895,44 @@ int parseconfigfile(){
 	warn="(including newline!) ";
       else
 	warn="";
-      printout(LOG_CRIT,"Error: line %d of file %s %sis more than than %d characters long.\n",
-	       lineno,CONFIGFILE,warn,MAXLINELEN);
+      printout(LOG_CRIT,"Error: line %d of file %s %sis more than %d characters.\n",
+	       contlineno,CONFIGFILE,warn,MAXLINELEN);
       exit(1); 
     }
-    
-    // eliminate any terminating newline
-    if (line[len-1]=='\n'){
-      len--;
-      line[len]='\0';
+
+    // Ignore anything after comment symbol
+    if ((comment=index(line,'#'))){
+      *comment='\0';
+      len=strlen(line);
     }
-    
-    // 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;
-    
-#if 0
-    // This is the start of some code to handle continuation
-    // characters in the /etc/smartd.conf file.  Note that these must
-    // be the final character on a line, with just a newline after
-    // them.  No other white space afterwards.
 
-    if (continuation+len>maxlinelen)
-      error;
+    // is the total line (made of all continuation lines) too long?
+    if (cont+len>MAXCONTLINE){
+      printout(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than %d characters.\n",
+	       lineno,contlineno,CONFIGFILE,MAXCONTLINE);
+      exit(1);
+    }
     
-    curr=config[entry].name+continuation;
-    strcpy(curr,dev);
-
+    // copy string so far into fullline, and increment length
+    strcpy(fullline+cont,line);
+    cont+=len;
 
-    if (cur[len-1]=='\ '){
-      cur[len-1]=' ';
-      continuation+=len;
+    // is this a continuation line.  If so, replace \ by space and look at next line
+    if ( (lastslash=rindex(line,'\\')) && !strtok(lastslash+1," \n\t")){
+      *(fullline+(cont-len)+(lastslash-line))=' ';
       continue;
     }
-    else {
-      continuation=0;
-      lineno++;
-    }
-#endif
 
-    // We've got a legit entry
-    if (entry>=MAXENTRIES){
-      printout(LOG_CRIT,"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++;
+    // Not a continuation line. Parse it
+    entry+=parseconfigline(entry,lineno,fullline);
+    lineno++;
+    cont=0;
   }
   fclose(fp);
   if (entry)
     return entry;
   
-  printout(LOG_CRIT,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
+  printout(LOG_CRIT,"Configuration file %s contains no devices (like /dev/hda)\n",CONFIGFILE);
   exit(1);
 }
 
@@ -571,12 +984,12 @@ void ParseOpts(int argc, char **argv){
     printhead();
     printout(LOG_INFO,copyleftstring);
     printout(LOG_INFO,"CVS version IDs of files used to build this code are:\n");
-    printone(out,CVSid3);
-    printout(LOG_INFO,"%s",out);
     printone(out,CVSid1);
     printout(LOG_INFO,"%s",out);
     printone(out,CVSid2);
     printout(LOG_INFO,"%s",out);
+    printone(out,CVSid6);
+    printout(LOG_INFO,"%s",out);
     exit(0);
   }
   
@@ -585,17 +998,85 @@ void ParseOpts(int argc, char **argv){
   return;
 }
 
+// Function we call if no configuration file was found.  It makes
+// entries for /dev/hd[a-l] and /dev/sd[a-z].
+int makeconfigentries(int num, char *name, int isata, int start){
+  int i;
+  
+  if (MAXENTRIES<(start+num)){
+    printout(LOG_CRIT,"Error: simulated config file can have no more than %d entries\n",MAXENTRIES);
+    exit(1);
+  }
+  
+  for(i=0; i<num; i++){
+    cfgfile *cfg=config+start+i;
+    
+    // clear all fields of structure
+    memset(cfg,0,sizeof(*cfg));
+    
+    // select if it's a SCSI or ATA device
+    cfg->tryata=isata;
+    cfg->tryscsi=!isata;
+    
+    // enable all possible tests
+    cfg->smartcheck=1;
+    cfg->prefail=1;
+    cfg->usagefailed=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+    
+    // lineno==0 is our clue that the device was not found in a
+    // config file!
+    cfg->lineno=0;
+    
+    // put in the device name
+    cfg->name=strdup(name);
+    cfg->failatt=(unsigned char *)calloc(32,1);
+    cfg->trackatt=(unsigned char *)calloc(32,1);
+    if (!cfg->name || !cfg->failatt || !cfg->trackatt) {
+      perror("no memory available to save name");
+      exit(1);
+    }
+    // increment final character of the name
+    cfg->name[strlen(name)-1]+=i;
+  }
+  return i;
+}
+
+
+void cantregister(char *name, char *type, int line){
+  if (line)
+    printout(LOG_INFO,"Unable to register %s device %s at line %d of file %s\n",
+	     type, name, line, CONFIGFILE);
+  else
+    printout(LOG_INFO,"Unable to register %s device %s\n",
+	     type, name);
+  return;
+}
+
+
 /* Main Program */
 int main (int argc, char **argv){
   atadevices_t atadevices[MAXATADEVICES], *atadevicesptr=atadevices;
   scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr=scsidevices;
   int i,entries;
+  atamainctrl control;
+  
+  // initialize global communications variables
+  con=&control;
+  memset(con,0,sizeof(control));
   
+  // initialize global counters
   numatadevices=numscsidevices=0;
   
   // Parse input and print header and usage info if needed
   ParseOpts(argc,argv);
- 
+  
+  // Do we mute printing from ataprint commands?
+  con->quietmode=0;
+  con->veryquietmode=debugmode?0:1;
+  
   // look in configuration file CONFIGFILE (normally /etc/smartd.conf)
   entries=parseconfigfile();
 
@@ -603,30 +1084,37 @@ int main (int argc, char **argv){
   if (!debugmode){
     daemon_init();
   }
+
+  // setup signal handler for shutdown
+  if (signal(SIGINT, sighandler)==SIG_IGN)
+    signal(SIGINT, SIG_IGN);
+  if (signal(SIGTERM, sighandler)==SIG_IGN)
+    signal(SIGTERM, SIG_IGN);
+  if (signal(SIGQUIT, sighandler)==SIG_IGN)
+    signal(SIGQUIT, SIG_IGN);
   
-  // If we found a config file, register its entries
-  if (entries)
-    for (i=0;i<entries;i++){
-      // register ATA devices
-      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);
-      // then register SCSI devices
-      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 {
-    // since there was no config file found, search all ATA and SCSI disks
-    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);
+  // install goobye message
+  atexit(goobye);
+  
+  // if there was no config file, create needed entries
+  if (!entries){
+    printout(LOG_INFO,"smartctl: file %s not found. Searching for devices.\n",CONFIGFILE);
+    entries+=makeconfigentries(MAXATADEVICES,"/dev/hda",1,entries);
+    entries+=makeconfigentries(MAXSCSIDEVICES,"/dev/sda",0,entries);
+  }
+  
+  // Register entries
+  for (i=0;i<entries;i++){
+    // register ATA devices
+    if (config[i].tryata && atadevicescan2(atadevicesptr+numatadevices, config+i))
+      cantregister(config[i].name, "ATA", config[i].lineno);
+    
+    // then register SCSI devices
+    if (config[i].tryscsi && scsidevicescan(scsidevicesptr, config[i].name))
+      cantregister(config[i].name, "SCSI", config[i].lineno);
   }
   
+  
   // Now start an infinite loop that checks all devices
   CheckDevices(atadevicesptr, scsidevicesptr); 
   return 0;
diff --git a/sm5/smartd.conf b/sm5/smartd.conf
index c4afbef0c..0114e5994 100644
--- a/sm5/smartd.conf
+++ b/sm5/smartd.conf
@@ -3,21 +3,41 @@
 # Sample configuration file for smartd.  See man 8 smartd.
 # Home page is: http://smartmontools.sourceforge.net
 
-# The file gives a list of devices to monitor using smartd with one
-# device per line. Since lines starting with a hash (#) are ignored, as are
-# blanks and tabs, this file only contains a single entry, for /dev/hda
+# The file gives a list of devices to monitor using smartd, with one
+# device per line. Text after a hash (#) is ignored, and you may use
+# spaces and tabs for white space. You may use '\' to continue lines.
 
-# You can usually identify which hard disks are on your system by looking
-# in /proc/ide and in /proc/scsi
+# You can usually identify which hard disks are on your system by
+# looking in /proc/ide and in /proc/scsi.
 
-# First (primary) ATA/IDE hard disk
-/dev/hda
+# First (primary) ATA/IDE hard disk.  Monitor all attributes
+/dev/hda -a
 
-# Other ATA/IDE hard disks
-# /dev/hdb
-# /dev/hdc
-# /dev/hdd
+# Monitor SMART status, ATA Error Log, Self-test log, and track
+# changes in all attributes except for attribute 194
+/dev/hdb -c l L -t -I 194 /dev/hdc
 
-# First two SCSI disks
-# /dev/sda
-# /dev/sdb
+# A very silent check.  Only report SMART health status if it fails
+/dev/hdc -c
+
+# First two SCSI disks.  Note that only the S directive applies
+/dev/sda -S
+/dev/sdb -S
+
+
+# HERE IS A LIST OF DIRECTIVES FOR THIS CONFIGURATION FILE
+#   -A    Device is an ATA device
+#   -S    Device is a SCSI device
+#   -c    Monitor SMART Health Status
+#   -l    Monitor SMART Error Log for changes
+#   -L    Monitor SMART Self-Test Log for new errors
+#   -f    Monitor for failure of any 'Usage' Attributes
+#   -p    Report changes in 'Prefailure' Attributes
+#   -u    Report changes in 'Usage' Attributes
+#   -t    Equivalent to -p and -u Directives
+#   -i ID Ignore Attribute ID for -f Directive
+#   -I ID Ignore Attribute ID for -p, -u or -t Directive
+#    #    Comment: text after a hash sign is ignored
+#    \    Line continuation character
+# Attribute ID is a decimal integer 1 <= ID <= 255
+# All but -S directive are only implemented for ATA devices
diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp
index 7e3fe7e40..6cb7d5156 100644
--- a/sm5/smartd.cpp
+++ b/sm5/smartd.cpp
@@ -20,8 +20,7 @@
  *
  */
 
-#include <errno.h>
-#include <stdlib.h>
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
@@ -32,15 +31,23 @@
 #include <linux/hdreg.h>
 #include <syslog.h>
 #include <stdarg.h>
-
+#include <signal.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "smartd.h"
+#include "ataprint.h"
+#include "extern.h"
 
 // CVS ID strings
 extern const char *CVSid1, *CVSid2;
-const char *CVSid3="$Id: smartd.cpp,v 1.36 2002/10/26 19:33:40 ballen4705 Exp $" 
-CVSID1 CVSID4 CVSID7;
+const char *CVSid6="$Id: smartd.cpp,v 1.37 2002/10/28 23:47:00 ballen4705 Exp $" 
+CVSID1 CVSID2 CVSID3 CVSID4 CVSID7;
+
+// global variable used for control of printing, passing arguments, etc.
+atamainctrl *con=NULL;
 
 // This function prints either to stdout or to the syslog as needed
 void printout(int priority,char *fmt, ...){
@@ -56,20 +63,33 @@ void printout(int priority,char *fmt, ...){
   return;
 }
 
-// Printing function for debugging atacmds. For debugging set 0 to 1
+// Printing function for debugging atacmds.
 // in #if statement
 void pout(char *fmt, ...){
   va_list ap;
-  
   // initialize variable argument list 
   va_start(ap,fmt);
-#if (0)
-  vprintf(fmt,ap);
-#endif
+  // in debug mode we will print the output from the ataprint.o functions!
+  if (debugmode)
+    vprintf(fmt,ap);
   va_end(ap);
   return;
 }
 
+void goobye(){
+  printout(LOG_CRIT,"smartd is exiting\n");
+  return;
+}
+
+volatile sig_atomic_t fatal = 0;
+// simple signal handler to print goodby message to syslog
+void sighandler(int sig){
+    printout(LOG_CRIT,"smartd received signal %d: %s\n",
+	     sig,strsignal(sig));
+    exit(1);
+}
+
+
 // Forks new process, closes all file descriptors, redirects stdin,
 // stdout, stderr
 int daemon_init(void){
@@ -78,16 +98,29 @@ int daemon_init(void){
 
   if ((pid=fork()) < 0) {
     // unable to fork!
-    printout(LOG_CRIT,"Unable to fork daemon process!\n");
+    printout(LOG_CRIT,"smartd unable to fork daemon process!\n");
     exit(1);
   }
   else if (pid)
     // we are the parent process -- exit cleanly
     exit(0);
   
-  // from here on, we are the child process
+  // from here on, we are the child process.
   setsid();
 
+
+  // Fork one more time to avoid any possibility of having terminals
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    printout(LOG_CRIT,"smartd unable to fork daemon process!\n");
+    exit(1);
+  }
+  else if (pid)
+    // we are the parent process -- exit cleanly
+    exit(0);
+
+  // Now we are the child's child...
+
   // close any open file descriptors
   for (i=getdtablesize();i>=0;--i)
     close(i);
@@ -111,13 +144,37 @@ void printhead(){
   return;
 }
 
+
+// prints help info for configuration file directives
+void Directives() {
+  printout(LOG_INFO,"Configuration file Directives (following device name):\n");
+  printout(LOG_INFO,"  -A    Device is an ATA device\n");
+  printout(LOG_INFO,"  -S    Device is a SCSI device\n");
+  printout(LOG_INFO,"  -c    Monitor SMART Health Status\n");
+  printout(LOG_INFO,"  -l    Monitor SMART Error Log for changes\n");
+  printout(LOG_INFO,"  -L    Monitor SMART Self-Test Log for new errors\n");
+  printout(LOG_INFO,"  -f    Monitor for failure of any 'Usage' Attributes\n");
+  printout(LOG_INFO,"  -p    Report changes in 'Prefailure' Attributes\n");
+  printout(LOG_INFO,"  -u    Report changes in 'Usage' Attributes\n");
+  printout(LOG_INFO,"  -t    Equivalent to -p and -u Directives\n");
+  printout(LOG_INFO,"  -i ID Ignore Attribute ID for -f Directive\n");
+  printout(LOG_INFO,"  -I ID Ignore Attribute ID for -p, -u or -t Directive\n");
+  printout(LOG_INFO,"   #    Comment: text after a hash sign is ignored\n");
+  printout(LOG_INFO,"   \\    Line continuation character\n");
+  printout(LOG_INFO,"Attribute ID is a decimal integer 1 <= ID <= 255\n");
+  printout(LOG_INFO,"All but -S directive are only implemented for ATA devices\n");
+  printout(LOG_INFO,"Example: /dev/hda -a\n");
+return;
+}
+
 /* prints help information for command syntax */
 void Usage (void){
   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\n",PRINTCOPYLEFT);
+  printout(LOG_INFO,"Command Line Options:\n");
+  printout(LOG_INFO,"  %c  Start smartd in debug Mode\n",DEBUGMODE);
+  printout(LOG_INFO,"  %c  Print License, Copyright, and version information\n\n",PRINTCOPYLEFT);
   printout(LOG_INFO,"Optional configuration file: %s\n",CONFIGFILE);
+  Directives();
 }
 
 // returns negative if problem, else fd>=0
@@ -125,9 +182,9 @@ int opendevice(char *device){
   int 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);
+      printout(LOG_INFO,"Device: %s, %s, open() failed\n",device, sys_errlist[errno]);
     else
-      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+      printout(LOG_INFO,"Device: %s, open() failed\n",device);
     return -1;
   }
   // device opened sucessfully
@@ -135,80 +192,150 @@ int opendevice(char *device){
 }
 
 // returns 1 if problem, else zero
-int closedevice(int fd){
+int closedevice(int fd, char *name){
   if (close(fd)){
     if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Closing file descriptor %d failed\n",sys_errlist[errno],fd);
+      printout(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, sys_errlist[errno], fd);
     else
-      printout(LOG_INFO,"Closing file descriptor %d failed\n",fd);
+      printout(LOG_INFO,"Device: %s, close(%d) failed\n",name,fd);
     return 1;
   }
-  // device opened sucessfully
+  // device sucessfully closed
   return 0;
 }
 
+// returns <0 on failure
+int ataerrorcount(int fd, char *name){
+  struct ata_smart_errorlog log;
+  
+  if (-1==ataReadErrorLog(fd,&log)){
+    printout(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of ATA errors
+  return log.error_log_pointer?log.ata_error_count:0;
+}
+
+// returns <0 if problem
+char selftesterrorcount(int fd, char *name){
+  struct ata_smart_selftestlog log;
+
+  if (-1==ataReadSelfTestLog(fd,&log)){
+    printout(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of self-test errors
+  return (char)ataPrintSmartSelfTestlog(log,0);
+}
+
+
+
 // scan to see what ata devices there are, and if they support SMART
-int atadevicescan (atadevices_t *devices, char *device){
+int atadevicescan2(atadevices_t *devices, cfgfile *cfg){
   int fd;
   struct hd_driveid drive;
+  char *device=cfg->name;
   
-  printout(LOG_INFO,"Opening device %s\n", device);
+  // should we try to register this as an ATA device?
+  if (!(cfg->tryata))
+    return 1;
+  
+  // open the device
   if ((fd=opendevice(device))<0)
     // device open failed
     return 1;
+  printout(LOG_INFO,"Device: %s, opened\n", device);
   
+  // Get drive identity structure
   if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
     // device exists, but not able to do SMART
+    printout(LOG_INFO,"Device: %s, not SMART capable, or couldn't enable SMART\n",device);
     close(fd);
-    printout(LOG_INFO,"Device: %s, Found but not SMART capable, or couldn't enable SMART\n",device);
-    return 2;
+    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;
+  // capability check: SMART status
+  if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
+    printout(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",device);
+    cfg->smartcheck=0;
   }
-  else if (ataReadSmartThresholds (fd,&devices[numatadevices].smartthres)){
+  
+  // capability check: Read smart values and thresholds
+  if (cfg->usagefailed || cfg->prefail || cfg->usage) {
+    devices->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values));
+    devices->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds));
+    
+    if (!devices->smartval || !devices->smartthres){
+      printout(LOG_CRIT,"Not enough memory to obtain SMART data\n");
+      exit(1);
+    }
+    
+    if (ataReadSmartValues(fd,devices->smartval) ||
+	ataReadSmartThresholds (fd,devices->smartthres)){
+      printout(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",device);
+      free(devices->smartval);
+      free(devices->smartthres);
+      cfg->usagefailed=cfg->prefail=cfg->usage=0;
+    }
+  }
+  
+  // capability check: self-test-log
+  if (cfg->selftest){
+    char val=selftesterrorcount(fd, device);
+    if (val>=0)
+      cfg->selflogcount=val;
+    else
+      cfg->selftest=0;
+  }
+  
+  // capability check: ATA error log
+  if (cfg->errorlog){
+    int val=ataerrorcount(fd, device);
+    if (val>=0)
+      cfg->ataerrorcount=val;
+    else
+      cfg->errorlog=0;
+  }
+  
+  // If not tests available or selected, return
+  if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || 
+	cfg->usagefailed || cfg->prefail || cfg->usage)) {
     close(fd);
-    printout(LOG_INFO,"Device: %s, Read SMART Thresholds Failed\n",device);
-    return 4;
+    return 3;
   }
   
-  // Device exists, and does SMART.  Add to list
+  // Do we still have entries available?
   if (numatadevices>=MAXATADEVICES){
     printout(LOG_CRIT,"smartd has found more than MAXATADEVICES=%d ATA devices.\n"
 	     "Recompile code from " PROJECTHOME " with larger MAXATADEVICES\n",numatadevices);
     exit(1);
   }
-
-  printout(LOG_INFO,"%s Found and is SMART capable. Adding to \"monitor\" list.\n",device);
-  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);
+  printout(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",device);
+  // no need to try sending SCSI commands to this device!
+  cfg->tryscsi=0;
+  
+  // we were called from a routine that has global storage for the name.  Keep pointer.
+  devices->devicename=device;
+  devices->cfg=cfg;
+  
   
   numatadevices++;
-  closedevice(fd);
+  closedevice(fd, device);
   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??
-int scsidevicescan (scsidevices_t *devices, char *device){
+// object?? Can anyone explain this obscurity?
+int scsidevicescan(scsidevices_t *devices, char *device){
   int i, fd, smartsupport;
   unsigned char  tBuf[4096];
   
-  printout(LOG_INFO,"Opening device %s\n", device);
+  printout(LOG_INFO,"Device: %s, opening\n", device);
   if ((fd=opendevice(device))<0)
     // device open failed
     return 1;
@@ -243,8 +370,10 @@ int scsidevicescan (scsidevices_t *devices, char *device){
   }
 
   // now we can proceed to register the device
-  printout(LOG_INFO, "Device: %s, Found and is SMART capable. Adding to \"monitor\" list.\n",device);
-  strcpy(devices[numscsidevices].devicename,device);
+  printout(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n",device);
+
+  // since device points to global memory, just keep that address
+  devices[numscsidevices].devicename=device;
 
   // register the supported functionality.  The smartd code does not
   // seem to make any further use of this information.
@@ -263,83 +392,195 @@ int scsidevicescan (scsidevices_t *devices, char *device){
     }	
   }
   numscsidevices++;
-  closedevice(fd);
+  closedevice(fd, device);
   return 0;
 }
 
+// We compare old and new values of the n'th attribute.  Note that n
+// is NOT the attribute ID number.. If equal, return 0.  The thre
+// structure is used to verify that the attributes are valid ones.  If
+// the new value is lower than the old value, then we return both old
+// and new values. new value=>lowest byte, old value=>next-to-lowest
+// byte, id value=>next-to-next-to-lowest byte., and prefail flag x as
+// bottom bit of highest byte.  See below (lsb on right)
 
-void ataCompareSmartValues (atadevices_t *device, struct ata_smart_values new ){
-    int i;
-    int oldval,newval,idold,idnew;
-    
-    for ( i =0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++){
-      // which device is it?
-      idnew=new.vendor_attributes[i].id;
-      idold=device->smartval.vendor_attributes[i].id;
-      
-      if (idold && idnew){
-	// if it's a valid attribute, compare values
-	newval=new.vendor_attributes[i].current;
-	oldval=device->smartval.vendor_attributes[i].current;
-	if (oldval!=newval){
-	  // values have changed; print them
-	  char *loc,attributename[64];
-	  loc=attributename;
-	  ataPrintSmartAttribName(attributename,idnew);
-	  // skip blank space in name
-	  while (*loc && *loc==' ')
-	    loc++;
-	  printout(LOG_INFO, "Device: %s, SMART Attribute: %s changed from %i to %i\n",
-		   device->devicename,loc,oldval,newval);
-	}
-      }
-    }
+//  [00000000x][attribute ID][old value][new value]
+int  ataCompareSmartValues2(struct ata_smart_values *new,
+			    struct ata_smart_values *old,
+			    struct ata_smart_thresholds *thresholds,
+			    int n){
+  struct ata_smart_attribute *now,*was;
+  struct ata_smart_threshold_entry *thre;
+  unsigned char oldval,newval;
+  int returnvalue;
+
+  // check that attribute number in range, and no null pointers
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thre)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  now=new->vendor_attributes+n;
+  was=old->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  // consider only valid attributes, with same valid ID in all structures
+  if (!now->id || !was->id || !thre->id || (now->id != was->id) || (now->id != thre->id))
+    return 0;
+
+  // if values have not changed, return
+  newval=now->current;
+  oldval=was->current;
+
+  // if any values out of the allowed range, or the values haven't changed, return
+  if (!newval || !oldval || newval>0xfe || oldval>0xfe || oldval==newval)
+    return 0;
+  
+  // values have changed.  Construct output
+  returnvalue=0;
+  returnvalue |= newval;
+  returnvalue |= oldval<<8;
+  returnvalue |= now->id<<16;
+  returnvalue |= (now->status.flag.prefailure)<<24;
+
+  return returnvalue;
 }
 
+// This looks to see if the corresponding bit of the 32 bytes is set.
+// This wastes a few bytes of storage but eliminates all searching and
+// sorting functions! Entry is ZERO <==> the attribute ON. Calling
+// with set=0 tells you if the attribute is being tracked or not.
+// Calling with set=1 turns the attribute OFF.
+int isattoff(unsigned char attr,unsigned char *data, int set){
+  // locate correct attribute
+  int loc=attr>>3;
+  int bit=attr & 0x07;
+  unsigned char mask=0x01<<bit;
 
-int ataCheckDevice( atadevices_t *drive){
-  struct ata_smart_values tempsmartval;
-  struct ata_smart_thresholds tempsmartthres;
-  int failed,fd;
-  char *loc,attributename[64];
+  // attribute zero is always OFF
+  if (!attr)
+    return 1;
 
+  if (!set)
+    return (data[loc] & mask);
+  
+  data[loc]|=mask;
+  // return value when setting makes no sense!
+  return 0;
+}
+
+
+int ataCheckDevice(atadevices_t *drive){
+  int fd,i;
+  char *name=drive->devicename;
+  cfgfile *cfg=drive->cfg;
+  
   // if we can't open device, fail gracefully rather than hard --
   // perhaps the next time around we'll be able to open it
-  if ((fd=opendevice(drive->devicename))<0)
+  if ((fd=opendevice(name))<0)
     return 1;
   
-  // Coming into this function, *drive contains the last values measured,
-  // and we read the NEW values into tempsmartval
-  if (ataReadSmartValues(fd,&tempsmartval))
-    printout(LOG_INFO, "%s:Failed to read SMART values\n", drive->devicename);
-  
-  // and we read the new thresholds into tempsmartthres
-  if (ataReadSmartThresholds(fd, &tempsmartthres))
-    printout(LOG_INFO, "%s:Failed to read SMART thresholds\n",drive->devicename);
-  
-  // See if any vendor attributes are below minimum, and print them
-  // out.  WHEN IT WORKS, we should here add a call to
-  // ataSmartStatus2() either in addition to or instead of the
-  // ataCheckSmart command below. This is the "right" long-term
-  // solution.
-  if ((failed=ataCheckSmart(tempsmartval,tempsmartthres,1))){
-    ataPrintSmartAttribName(attributename,failed);
-    // skip blank space in name
-    loc=attributename;
-    while (*loc && *loc==' ')
-      loc++;
-    printout(LOG_CRIT,"Device: %s, Failed SMART attribute: %s. Use smartctl -a %s.\n",
-	     drive->devicename,loc,drive->devicename);
-  }
-  
-  // see if any values have changed.  Second argument is new values
-  ataCompareSmartValues(drive, tempsmartval);
-  
-  // Save the new values into *drive for the next time around
-  drive->smartval = tempsmartval;
-  drive->smartthres = tempsmartthres;
-  
-  closedevice(fd);
+  // check smart status
+  if (cfg->smartcheck){
+    int status=ataSmartStatus2(fd);
+    if (status==-1)
+      printout(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
+    else if (status==1)
+      printout(LOG_INFO,"Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n",name);
+  }
+  
+  // Check everything that depends upon SMART Data (eg, Attribute values)
+  if (cfg->usagefailed || cfg->prefail || cfg->usage){
+    struct ata_smart_values     curval;
+    struct ata_smart_thresholds *thresh=drive->smartthres;
+    
+    // Read current attribute values. *drive contains old values adn thresholds
+    if (ataReadSmartValues(fd,&curval))
+      printout(LOG_INFO, "Device: %s, failed to read SMART Attribute Data\n", name);
+    else {  
+      // look for failed usage attributes, or track usage or prefail attributes
+      for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+	int att;
+	
+	// This block looks for usage attributes that have failed.
+	// Prefail attributes that have failed are returned with a
+	// positive sign. No failure returns 0. Usage attributes<0.
+	if (cfg->usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){
+	  
+	  // are we tracking this attribute?
+	  att *= -1;
+	  if (!isattoff(att, cfg->failatt, 0)){
+	    char attname[64], *loc=attname;
+	    
+	    // get attribute name & skip white space
+	    ataPrintSmartAttribName(loc, att);
+	    while (*loc && *loc==' ') loc++;
+	    
+	    // warning message
+	    printout(LOG_CRIT,"Device: %s, Failed SMART usage attribute: %s. Use smartctl -v %s.\n", name, loc, name);
+	  }
+	}
+	
+	// This block tracks usage or prefailure attributes to see if they are changing
+	if ((cfg->usage || cfg->prefail) && ((att=ataCompareSmartValues2(&curval, drive->smartval, thresh, i)))){
+	  const int mask=0xff;
+	  int prefail=(att>>24) & mask;
+	  int id     =(att>>16) & mask;
+	  int oldval =(att>>8)  & mask;
+	  int newval =(att>>0)  & mask;
+	  char attname[64],*loc=attname;
+	  
+	  // are we tracking this attribute?
+	  if (!isattoff(id, cfg->trackatt, 0)){
+	    
+	    // get attribute name, skip spaces
+	    ataPrintSmartAttribName(loc, id);
+	    while (*loc && *loc==' ') loc++;
+	    
+	    // prefailure attribute
+	    if (cfg->prefail && prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %i to %i\n",
+		       name, loc, oldval, newval);
+
+	    // usage attribute
+	    if (cfg->usage && !prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %i to %i\n",
+		       name, loc, oldval, newval);
+	  }
+	} // endof block tracking usage or prefailure
+      } // end of loop over attributes
+     
+      // Save the new values into *drive for the next time around
+      memcpy(drive->smartval,&curval,sizeof(curval));
+    } 
+  }
+  
+  // check if number of selftest errors has increased (note: may also DECREASE)
+  if (cfg->selftest){
+    char old=cfg->selflogcount;
+    char new=selftesterrorcount(fd, name);
+    if (new>old){
+      printout(LOG_CRIT,"Device: %s, Self-Test Log error count increased from %d to %d\n",
+	       name,old,new);
+    }
+    if (new>=0)
+      // Needed suince self-test error count may  DECREASE
+      cfg->selflogcount=new;
+  }
+
+  
+  // check if number of ATA errors has increased
+  if (cfg->errorlog){
+    int old=cfg->ataerrorcount;
+    int new=ataerrorcount(fd, name);
+    if (new>old){
+      printout(LOG_CRIT,"Device: %s, ATA error count increased from %d to %d\n",
+	       name,old,new);
+    }
+    // this last line is probably not needed, count always increases
+    if (new>=0)
+      cfg->ataerrorcount=new;
+  }
+  closedevice(fd, name);
   return 0;
 }
 
@@ -359,7 +600,7 @@ int scsiCheckDevice( scsidevices_t *drive){
   currenttemp = triptemp = 0;
   
   if (scsiCheckSmart(fd, drive->SmartPageSupported, &returnvalue, &currenttemp, &triptemp))
-    printout(LOG_INFO, "%s:Failed to read SMART values\n", drive->devicename);
+    printout(LOG_INFO, "Device: %s, failed to read SMART values\n", drive->devicename);
   
   if (returnvalue)
     printout(LOG_CRIT, "Device: %s, SMART Failure: (%02x) %s\n", drive->devicename, 
@@ -375,11 +616,11 @@ int scsiCheckDevice( scsidevices_t *drive){
 	       drive->devicename, (int) (currenttemp - drive->Temperature), (unsigned int) currenttemp );
     drive->Temperature = currenttemp;
   }
-  closedevice(fd);
+  closedevice(fd, drive->devicename);
   return 0;
 }
 
-void CheckDevices (  atadevices_t *atadevices, scsidevices_t *scsidevices){
+void CheckDevices(atadevices_t *atadevices, scsidevices_t *scsidevices){
   int i;
   
   // If there are no devices to monitor, then exit
@@ -410,12 +651,197 @@ char copyleftstring[]=
 cfgfile config[MAXENTRIES];
 
 
-// returns number of entries in config file, or 0 if no config file exists
+int parsetoken(char *token,cfgfile *cfg){
+  char sym=token[1];
+  char *name=cfg->name;
+  int lineno=cfg->lineno;
+  char *delim=" \n\t";
+
+  // is the rest of the line a comment
+  if (*token=='#')
+    return 1;
+  
+  // is the token not recognized?
+  if (*token!='-' || strlen(token)!=2) {
+    printout(LOG_CRIT,"Drive: %s, unknown Directive: %s at line %d of file %s\n",
+	     name,token,lineno,CONFIGFILE);
+    Directives();
+    exit(1);
+  }
+  
+  // let's parse the token and swallow its argument
+  switch (sym) {
+    char *arg;
+    char *endptr;
+    int val;
+    
+  case 'A':
+    // ATA device
+    cfg->tryata=1;
+    cfg->tryscsi=0;
+    break;
+  case 'S':
+    //SCSI device
+    cfg->tryscsi=1;
+    cfg->tryata=0;
+    break;
+  case 'c':
+    // check SMART status
+    cfg->smartcheck=1;
+    break;
+  case 'f':
+    // check for failure of usage attributes
+    cfg->usagefailed=1;
+    break;
+  case 't':
+    // track changes in all vendor attributes
+    cfg->prefail=1;
+    cfg->usage=1;
+    break;
+  case 'p':
+    // track changes in prefail vendor attributes
+    cfg->prefail=1;
+    break;
+  case 'u':
+    //  track changes in usage vendor attributes
+    cfg->usage=1;
+    break;
+  case 'L':
+    // track changes in self-test log
+    cfg->selftest=1;
+    break;
+  case 'l':
+    // track changes ATA error log
+    cfg->errorlog=1;
+    break;
+  case 'a':
+    // monitor everything
+    cfg->smartcheck=1;
+    cfg->prefail=1;
+    cfg->usagefailed=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+    break;
+  case 'i':
+  case 'I':
+    // ignore a particular vendor attribute for tracking (i) or
+    // failure (I)
+    arg=strtok(NULL,delim);
+    // make sure argument is there
+    if (!arg) {
+      printout(LOG_CRIT,"Drive %s Directive: %s at line %d of file %s needs integer argument.\n",
+	       name,token,lineno,CONFIGFILE);
+      Directives();
+      exit(1);
+    }
+    // get argument value, check that it's properly-formed, an
+    // integer, and in-range
+    val=strtol(arg,&endptr,10);
+    if (*endptr!='\0' || val<=0 || val>255)  {
+      printout(LOG_CRIT,"Drive %s Directive: %s at line %d of file %s has argument: %s, needs 0 < n < 256\n",
+	       name,token,lineno,CONFIGFILE,arg);
+      Directives();
+      exit(1);
+    }
+    // put into correct list (bitmaps, access only with isattoff()
+    // function. Turns OFF corresponding attribute.
+    if (sym=='I')
+      isattoff(val,cfg->trackatt,1);
+    else
+      isattoff(val,cfg->failatt,1);
+    break;
+  default:
+    printout(LOG_CRIT,"Drive: %s, unknown option: %s at line %d of file %s\n",
+	     name,token,lineno,CONFIGFILE);
+    Directives();
+    exit(1);
+  }
+  return 1;
+}
+
+
+int parseconfigline(int entry, int lineno,char *line){
+  char *token,*copy;
+  char *delim=" \n\t";
+  char *name;
+  int len;
+  cfgfile *cfg;
+  
+  if (!(copy=strdup(line))){
+    perror("no memory available to parse line");
+    exit(1);
+  }
+  
+  // get first token -- device name
+  if (!(name=strtok(copy,delim)) || *name=='#'){
+    free(copy);
+    return 0;
+  }
+
+  // Is there space for another entry?
+  if (entry>=MAXENTRIES){
+    printout(LOG_CRIT,"Error: configuration file %s can have no more than %d entries\n",
+	     CONFIGFILE,MAXENTRIES);
+    exit(1);
+  }
+
+  // We've got a legit entry, clear structure
+  cfg=config+entry;
+  memset(cfg,0,sizeof(*config));
+
+  // Save info to process memory for after forking 32 bytes contains 1
+  // bit per possible attribute ID.  See isattoff()
+  cfg->name=strdup(name);
+  cfg->failatt=(unsigned char *)calloc(32,1);
+  cfg->trackatt=(unsigned char *)calloc(32,1);
+  
+  if (!cfg->name || !cfg->failatt || !cfg->trackatt) {
+    perror("no memory available to save name");
+    exit(1);
+  }
+
+  cfg->lineno=lineno;
+  cfg->tryscsi=cfg->tryata=1;
+  
+  // Try and recognize if a IDE or SCSI device.  These can be
+  // overwritten by configuration file directives.
+  len=strlen(name);
+  if (len>5 && !strncmp("/dev/h",name, 6))
+    cfg->tryscsi=0;
+  
+  if (len>5 && !strncmp("/dev/s",name, 6))
+    cfg->tryata=0;
+
+  // parse tokens one at a time from the file
+  while ((token=strtok(NULL,delim)) && parsetoken(token,cfg)){
+#if 0
+  printout(LOG_INFO,"Parsed token %s\n",token);
+#endif
+  }
+
+  // basic sanity check -- are any options turned on?
+  if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail || cfg->usage || cfg->selftest || cfg->errorlog || cfg->tryscsi)){
+    printout(LOG_CRIT,"Drive: %s, no monitoring Directives on line %d of file %s\n",
+	     cfg->name, cfg->lineno, CONFIGFILE);
+    Directives();
+    exit(1);
+  }
+
+  entry++;
+  free(copy);
+  return 1;
+}
+
+// returns number of entries in config file, or 0 if no config file
+// exists.  A config file with zero entries will cause an error
+// message and an exit.
 int parseconfigfile(){
   FILE *fp;
-  int entry=0,lineno=0;
+  int entry=0,lineno=1,cont=0,contlineno=0;
   char line[MAXLINELEN+2];
-  
+  char fullline[MAXCONTLINE+1];
+
   // Open config file, if it exists
   fp=fopen(CONFIGFILE,"r");
   if (fp==NULL && errno!=ENOENT){
@@ -432,14 +858,34 @@ int parseconfigfile(){
   if (fp==NULL)
     return 0;
   
-  // configuration file exists.  Read it and search for devices
+  // configuration file exists
   printout(LOG_INFO,"Using configuration file %s\n",CONFIGFILE);
-  while (fgets(line,MAXLINELEN+2,fp)){
-    int len;
-    char *dev;
+
+  // parse config file line by line
+  while (1) {
+    int len=0;
+    char *lastslash;
+    char *comment;
+    char *code;
+
+    // make debugging simpler
+    memset(line,0,sizeof(line));
+
+    // get a line
+    code=fgets(line,MAXLINELEN+2,fp);
     
-    // track linenumber for error messages
-    lineno++;
+    // are we at the end of the file?
+    if (!code){
+      if (cont) {
+	// the final line is part of a continuation line
+	cont=0;
+	entry+=parseconfigline(entry,lineno,fullline);
+      }
+      break;
+    }
+
+    // input file line number
+    contlineno++;
     
     // See if line is too long
     len=strlen(line);
@@ -449,77 +895,44 @@ int parseconfigfile(){
 	warn="(including newline!) ";
       else
 	warn="";
-      printout(LOG_CRIT,"Error: line %d of file %s %sis more than than %d characters long.\n",
-	       lineno,CONFIGFILE,warn,MAXLINELEN);
+      printout(LOG_CRIT,"Error: line %d of file %s %sis more than %d characters.\n",
+	       contlineno,CONFIGFILE,warn,MAXLINELEN);
       exit(1); 
     }
-    
-    // eliminate any terminating newline
-    if (line[len-1]=='\n'){
-      len--;
-      line[len]='\0';
+
+    // Ignore anything after comment symbol
+    if ((comment=index(line,'#'))){
+      *comment='\0';
+      len=strlen(line);
     }
-    
-    // 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;
-    
-#if 0
-    // This is the start of some code to handle continuation
-    // characters in the /etc/smartd.conf file.  Note that these must
-    // be the final character on a line, with just a newline after
-    // them.  No other white space afterwards.
 
-    if (continuation+len>maxlinelen)
-      error;
+    // is the total line (made of all continuation lines) too long?
+    if (cont+len>MAXCONTLINE){
+      printout(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than %d characters.\n",
+	       lineno,contlineno,CONFIGFILE,MAXCONTLINE);
+      exit(1);
+    }
     
-    curr=config[entry].name+continuation;
-    strcpy(curr,dev);
-
+    // copy string so far into fullline, and increment length
+    strcpy(fullline+cont,line);
+    cont+=len;
 
-    if (cur[len-1]=='\ '){
-      cur[len-1]=' ';
-      continuation+=len;
+    // is this a continuation line.  If so, replace \ by space and look at next line
+    if ( (lastslash=rindex(line,'\\')) && !strtok(lastslash+1," \n\t")){
+      *(fullline+(cont-len)+(lastslash-line))=' ';
       continue;
     }
-    else {
-      continuation=0;
-      lineno++;
-    }
-#endif
 
-    // We've got a legit entry
-    if (entry>=MAXENTRIES){
-      printout(LOG_CRIT,"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++;
+    // Not a continuation line. Parse it
+    entry+=parseconfigline(entry,lineno,fullline);
+    lineno++;
+    cont=0;
   }
   fclose(fp);
   if (entry)
     return entry;
   
-  printout(LOG_CRIT,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
+  printout(LOG_CRIT,"Configuration file %s contains no devices (like /dev/hda)\n",CONFIGFILE);
   exit(1);
 }
 
@@ -571,12 +984,12 @@ void ParseOpts(int argc, char **argv){
     printhead();
     printout(LOG_INFO,copyleftstring);
     printout(LOG_INFO,"CVS version IDs of files used to build this code are:\n");
-    printone(out,CVSid3);
-    printout(LOG_INFO,"%s",out);
     printone(out,CVSid1);
     printout(LOG_INFO,"%s",out);
     printone(out,CVSid2);
     printout(LOG_INFO,"%s",out);
+    printone(out,CVSid6);
+    printout(LOG_INFO,"%s",out);
     exit(0);
   }
   
@@ -585,17 +998,85 @@ void ParseOpts(int argc, char **argv){
   return;
 }
 
+// Function we call if no configuration file was found.  It makes
+// entries for /dev/hd[a-l] and /dev/sd[a-z].
+int makeconfigentries(int num, char *name, int isata, int start){
+  int i;
+  
+  if (MAXENTRIES<(start+num)){
+    printout(LOG_CRIT,"Error: simulated config file can have no more than %d entries\n",MAXENTRIES);
+    exit(1);
+  }
+  
+  for(i=0; i<num; i++){
+    cfgfile *cfg=config+start+i;
+    
+    // clear all fields of structure
+    memset(cfg,0,sizeof(*cfg));
+    
+    // select if it's a SCSI or ATA device
+    cfg->tryata=isata;
+    cfg->tryscsi=!isata;
+    
+    // enable all possible tests
+    cfg->smartcheck=1;
+    cfg->prefail=1;
+    cfg->usagefailed=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+    
+    // lineno==0 is our clue that the device was not found in a
+    // config file!
+    cfg->lineno=0;
+    
+    // put in the device name
+    cfg->name=strdup(name);
+    cfg->failatt=(unsigned char *)calloc(32,1);
+    cfg->trackatt=(unsigned char *)calloc(32,1);
+    if (!cfg->name || !cfg->failatt || !cfg->trackatt) {
+      perror("no memory available to save name");
+      exit(1);
+    }
+    // increment final character of the name
+    cfg->name[strlen(name)-1]+=i;
+  }
+  return i;
+}
+
+
+void cantregister(char *name, char *type, int line){
+  if (line)
+    printout(LOG_INFO,"Unable to register %s device %s at line %d of file %s\n",
+	     type, name, line, CONFIGFILE);
+  else
+    printout(LOG_INFO,"Unable to register %s device %s\n",
+	     type, name);
+  return;
+}
+
+
 /* Main Program */
 int main (int argc, char **argv){
   atadevices_t atadevices[MAXATADEVICES], *atadevicesptr=atadevices;
   scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr=scsidevices;
   int i,entries;
+  atamainctrl control;
+  
+  // initialize global communications variables
+  con=&control;
+  memset(con,0,sizeof(control));
   
+  // initialize global counters
   numatadevices=numscsidevices=0;
   
   // Parse input and print header and usage info if needed
   ParseOpts(argc,argv);
- 
+  
+  // Do we mute printing from ataprint commands?
+  con->quietmode=0;
+  con->veryquietmode=debugmode?0:1;
+  
   // look in configuration file CONFIGFILE (normally /etc/smartd.conf)
   entries=parseconfigfile();
 
@@ -603,30 +1084,37 @@ int main (int argc, char **argv){
   if (!debugmode){
     daemon_init();
   }
+
+  // setup signal handler for shutdown
+  if (signal(SIGINT, sighandler)==SIG_IGN)
+    signal(SIGINT, SIG_IGN);
+  if (signal(SIGTERM, sighandler)==SIG_IGN)
+    signal(SIGTERM, SIG_IGN);
+  if (signal(SIGQUIT, sighandler)==SIG_IGN)
+    signal(SIGQUIT, SIG_IGN);
   
-  // If we found a config file, register its entries
-  if (entries)
-    for (i=0;i<entries;i++){
-      // register ATA devices
-      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);
-      // then register SCSI devices
-      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 {
-    // since there was no config file found, search all ATA and SCSI disks
-    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);
+  // install goobye message
+  atexit(goobye);
+  
+  // if there was no config file, create needed entries
+  if (!entries){
+    printout(LOG_INFO,"smartctl: file %s not found. Searching for devices.\n",CONFIGFILE);
+    entries+=makeconfigentries(MAXATADEVICES,"/dev/hda",1,entries);
+    entries+=makeconfigentries(MAXSCSIDEVICES,"/dev/sda",0,entries);
+  }
+  
+  // Register entries
+  for (i=0;i<entries;i++){
+    // register ATA devices
+    if (config[i].tryata && atadevicescan2(atadevicesptr+numatadevices, config+i))
+      cantregister(config[i].name, "ATA", config[i].lineno);
+    
+    // then register SCSI devices
+    if (config[i].tryscsi && scsidevicescan(scsidevicesptr, config[i].name))
+      cantregister(config[i].name, "SCSI", config[i].lineno);
   }
   
+  
   // Now start an infinite loop that checks all devices
   CheckDevices(atadevicesptr, scsidevicesptr); 
   return 0;
diff --git a/sm5/smartd.h b/sm5/smartd.h
index bf0fdd3f6..2b1febf47 100644
--- a/sm5/smartd.h
+++ b/sm5/smartd.h
@@ -23,13 +23,15 @@
  */
 
 #ifndef CVSID7
-#define CVSID7 "$Id: smartd.h,v 1.11 2002/10/26 10:19:16 ballen4705 Exp $\n"
+#define CVSID7 "$Id: smartd.h,v 1.12 2002/10/28 23:47:00 ballen4705 Exp $\n"
 #endif
 
 // Configuration file
 #define CONFIGFILE "/etc/smartd.conf"
-#define MAXLINELEN 114
+#define MAXLINELEN 128
 #define MAXENTRIES 64
+#define MAXCONTLINE 511
+#define MAXDEVLEN 51
 
 // BAD PROGRAMMING - GLOBAL VARIABLES SHOULD BE IN .c NOT .h FILE
 
@@ -58,24 +60,49 @@ unsigned char debugmode               = FALSE;
 unsigned char emailnotification       = FALSE;
 unsigned char printcopyleft           = FALSE;
 
-typedef struct atadevices_s {
-	int selftest;
-	struct hd_driveid drive;
-	struct ata_smart_values smartval;
-	struct ata_smart_thresholds smartthres;
-	char devicename[MAXLINELEN+2];
-}  atadevices_t;
 
 typedef struct scsidevices_s {
-	unsigned char SmartPageSupported;
-	unsigned char TempPageSupported;
-	unsigned char Temperature;
-	char devicename[MAXLINELEN+2];
+  unsigned char SmartPageSupported;
+  unsigned char TempPageSupported;
+  unsigned char Temperature;
+  char *devicename;
 } scsidevices_t;
 
+
 typedef struct configfile_s {
+  // which line was entry in file; what device type and name?
   int lineno;
-  int tryata;
-  int tryscsi;
-  char name[MAXLINELEN+2];  // really only needs to be +1
+  char tryata;
+  char tryscsi;
+  char *name;
+
+  // which tests have been enabled?
+  char smartcheck;
+  char usagefailed;
+  char prefail;
+  char usage;
+  char selftest;
+  char errorlog;
+
+  // store counts of ata and self-test errors
+  char selflogcount;
+  int  ataerrorcount;
+  // following two items point to 32 bytes, in the form of are
+  // 32x8=256 single bit flags 
+
+  // valid attribute numbers are from 1 <= x <= 255
+  // valid attribute values  are from 1 <= x <= 254
+  unsigned char *failatt;
+  unsigned char *trackatt;
 } cfgfile;
+
+
+typedef struct atadevices_s {
+  struct ata_smart_values *smartval;
+  struct ata_smart_thresholds *smartthres;
+  cfgfile *cfg;
+  char *devicename;
+}  atadevices_t;
+
+
+int ataCheckDevice(atadevices_t *drive);
-- 
GitLab