From ad1a0a5fb4256be1d1d4476cf5486cfa0a55aae5 Mon Sep 17 00:00:00 2001
From: ballen4705 <ballen4705@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Thu, 6 Mar 2003 06:28:47 +0000
Subject: [PATCH] [BA] smartd: Added -r ID and -R ID for reporting/tracking Raw
        values of Attributes.

[BA] smartd/smartctl: Changed printing of spin-up-time attribute
       raw value to reflect current/average as per IBM standard.


git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@508 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/CHANGELOG          |   8 ++-
 sm5/atacmds.c          |  84 +++++++++++++++++++++++++-
 sm5/atacmds.cpp        |  84 +++++++++++++++++++++++++-
 sm5/atacmds.h          |   8 ++-
 sm5/ataprint.c         |  58 ++----------------
 sm5/ataprint.cpp       |  58 ++----------------
 sm5/smartd.8           |  68 ++++++++++++++++++---
 sm5/smartd.c           | 133 ++++++++++++++++++++++++-----------------
 sm5/smartd.conf        |   6 +-
 sm5/smartd.conf.5      |  68 ++++++++++++++++++---
 sm5/smartd.cpp         | 133 ++++++++++++++++++++++++-----------------
 sm5/smartd.h           |  24 ++++++--
 sm5/smartmontools.spec |   4 +-
 13 files changed, 493 insertions(+), 243 deletions(-)

diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index e5840e53e..3568405cb 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.96 2003/02/24 15:51:30 ballen4705 Exp $
+$Id: CHANGELOG,v 1.97 2003/03/06 06:28:46 ballen4705 Exp $
 
 Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
@@ -33,6 +33,12 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 CURRENT RELEASE (see VERSION file in this directory):
 
+  [BA] smartd: Added -r ID and -R ID for reporting/tracking Raw
+       values of Attributes.
+
+  [BA] smartd/smartctl: Changed printing of spin-up-time attribute
+       raw value to reflect current/average as per IBM standard.
+
   [BA] smartd/smartctl: Added -v 9,seconds option for disks which
        use Attribute 9 for power-on lifetime in seconds.
 
diff --git a/sm5/atacmds.c b/sm5/atacmds.c
index f3d90127f..8bf8d8f89 100644
--- a/sm5/atacmds.c
+++ b/sm5/atacmds.c
@@ -30,7 +30,7 @@
 #include "atacmds.h"
 #include "utility.h"
 
-const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.54 2003/02/24 15:51:31 ballen4705 Exp $" ATACMDS_H_CVSID UTILITY_H_CVSID;
+const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.55 2003/03/06 06:28:47 ballen4705 Exp $" ATACMDS_H_CVSID UTILITY_H_CVSID;
 
 // 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
@@ -782,6 +782,88 @@ int ataCheckAttribute(struct ata_smart_values *data,
     return -1*(disk->id);
 }
 
+
+// This routine prints the raw value of an attribute as a text string
+// into out. It also returns this 48-bit number as a long long.  The
+// array defs[] contains non-zero values if particular attributes have
+// non-default interpretations.
+
+long long ataPrintSmartAttribRawValue(char *out, 
+				      struct ata_smart_attribute *attribute,
+				      unsigned char *defs){
+  long long rawvalue;
+  int j;
+  
+  // convert the six individual bytes to a long long (8 byte) integer.
+  // This is the value that we'll eventually return.
+  rawvalue = 0;
+  for (j=0; j<6; j++) {
+    // This looks a bit roundabout, but is necessary.  Don't
+    // succumb to the temptation to use raw[j]<<(8*j) since under
+    // the normal rules this will be promoted to the native type.
+    // On a 32 bit machine this might then overflow.
+    long long temp;
+    temp = attribute->raw[j];
+    temp <<= 8*j;
+    rawvalue |= temp;
+  }
+  
+  // This switch statement is where we handle Raw attributes
+  // that are stored in an unusual vendor-specific format,
+  switch (attribute->id){
+    // Spin-up time
+  case 3:
+    {
+      int i, spin[2];
+      // construct two twy-byte quantities, print first
+      for (i=0; i<2; i++){
+	spin[i] = attribute->raw[2*i+1];
+	spin[i] <<= 8;
+	spin[i] |= attribute->raw[2*i];
+      }
+      out+=sprintf(out, "%d", spin[0]);
+      
+      // if second nonzero then it stores the average spin-up time
+      if (spin[1])
+	sprintf(out, " (Average %d)", spin[1]);
+    }
+    break;
+    // Power on time
+  case 9:
+    if (defs[9]==1){
+      // minutes
+      long long tmp1=rawvalue/60;
+      long long tmp2=rawvalue%60;
+      sprintf(out, "%lluh+%02llum", tmp1, tmp2);
+    }
+    else if (defs[9]==3){
+      // seconds
+      long long hours=rawvalue/3600;
+      long long minutes=(rawvalue-3600*hours)/60;
+      long long seconds=rawvalue%60;
+      sprintf(out, "%lluh+%02llum+%02llus", hours, minutes, seconds);
+    }
+    else
+      // hours
+      sprintf(out, "%llu", rawvalue);  //stored in hours
+    break;
+    // Temperature
+  case 194:
+    out+=sprintf(out, "%d", (int)attribute->raw[0]);
+    if (!(rawvalue==attribute->raw[0]))
+      // The other bytes are in use. Try IBM's model
+      sprintf(out, " (Lifetime Min/Max %d/%d)",(int)attribute->raw[2],
+	      (int)attribute->raw[4]);
+    break;
+  default:
+    sprintf(out, "%llu", rawvalue);
+  }
+
+  // Return the full value
+  return rawvalue;
+}
+
+
 // Note some attribute names appear redundant because different
 // manufacturers use different attribute IDs for an attribute with the
 // same name.  The array defs[] contains non-zero values if particular
diff --git a/sm5/atacmds.cpp b/sm5/atacmds.cpp
index 19578e85f..3f4b4b032 100644
--- a/sm5/atacmds.cpp
+++ b/sm5/atacmds.cpp
@@ -30,7 +30,7 @@
 #include "atacmds.h"
 #include "utility.h"
 
-const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.54 2003/02/24 15:51:31 ballen4705 Exp $" ATACMDS_H_CVSID UTILITY_H_CVSID;
+const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.55 2003/03/06 06:28:47 ballen4705 Exp $" ATACMDS_H_CVSID UTILITY_H_CVSID;
 
 // 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
@@ -782,6 +782,88 @@ int ataCheckAttribute(struct ata_smart_values *data,
     return -1*(disk->id);
 }
 
+
+// This routine prints the raw value of an attribute as a text string
+// into out. It also returns this 48-bit number as a long long.  The
+// array defs[] contains non-zero values if particular attributes have
+// non-default interpretations.
+
+long long ataPrintSmartAttribRawValue(char *out, 
+				      struct ata_smart_attribute *attribute,
+				      unsigned char *defs){
+  long long rawvalue;
+  int j;
+  
+  // convert the six individual bytes to a long long (8 byte) integer.
+  // This is the value that we'll eventually return.
+  rawvalue = 0;
+  for (j=0; j<6; j++) {
+    // This looks a bit roundabout, but is necessary.  Don't
+    // succumb to the temptation to use raw[j]<<(8*j) since under
+    // the normal rules this will be promoted to the native type.
+    // On a 32 bit machine this might then overflow.
+    long long temp;
+    temp = attribute->raw[j];
+    temp <<= 8*j;
+    rawvalue |= temp;
+  }
+  
+  // This switch statement is where we handle Raw attributes
+  // that are stored in an unusual vendor-specific format,
+  switch (attribute->id){
+    // Spin-up time
+  case 3:
+    {
+      int i, spin[2];
+      // construct two twy-byte quantities, print first
+      for (i=0; i<2; i++){
+	spin[i] = attribute->raw[2*i+1];
+	spin[i] <<= 8;
+	spin[i] |= attribute->raw[2*i];
+      }
+      out+=sprintf(out, "%d", spin[0]);
+      
+      // if second nonzero then it stores the average spin-up time
+      if (spin[1])
+	sprintf(out, " (Average %d)", spin[1]);
+    }
+    break;
+    // Power on time
+  case 9:
+    if (defs[9]==1){
+      // minutes
+      long long tmp1=rawvalue/60;
+      long long tmp2=rawvalue%60;
+      sprintf(out, "%lluh+%02llum", tmp1, tmp2);
+    }
+    else if (defs[9]==3){
+      // seconds
+      long long hours=rawvalue/3600;
+      long long minutes=(rawvalue-3600*hours)/60;
+      long long seconds=rawvalue%60;
+      sprintf(out, "%lluh+%02llum+%02llus", hours, minutes, seconds);
+    }
+    else
+      // hours
+      sprintf(out, "%llu", rawvalue);  //stored in hours
+    break;
+    // Temperature
+  case 194:
+    out+=sprintf(out, "%d", (int)attribute->raw[0]);
+    if (!(rawvalue==attribute->raw[0]))
+      // The other bytes are in use. Try IBM's model
+      sprintf(out, " (Lifetime Min/Max %d/%d)",(int)attribute->raw[2],
+	      (int)attribute->raw[4]);
+    break;
+  default:
+    sprintf(out, "%llu", rawvalue);
+  }
+
+  // Return the full value
+  return rawvalue;
+}
+
+
 // Note some attribute names appear redundant because different
 // manufacturers use different attribute IDs for an attribute with the
 // same name.  The array defs[] contains non-zero values if particular
diff --git a/sm5/atacmds.h b/sm5/atacmds.h
index 1e4e8eed4..b1094a561 100644
--- a/sm5/atacmds.h
+++ b/sm5/atacmds.h
@@ -26,7 +26,7 @@
 #define _ATACMDS_H_
 
 #ifndef ATACMDS_H_CVSID
-#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.33 2003/01/17 12:20:24 ballen4705 Exp $\n"
+#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.34 2003/03/06 06:28:47 ballen4705 Exp $\n"
 #endif
 
 // These are the major and minor versions for smartd and smartctl
@@ -341,6 +341,12 @@ int ataSmartTest(int device, int testtype);
 
 int TestTime(struct ata_smart_values *data,int testtype);
 
+// Prints the raw value (with appropriate formatting) into the
+// character string out.
+long long ataPrintSmartAttribRawValue(char *out, 
+				      struct ata_smart_attribute *attribute,
+				      unsigned char *defs);
+
 // Prints Attribute Name for standard SMART attributes. Writes a
 // 30 byte string with attribute name into output
 void ataPrintSmartAttribName(char *output, unsigned char id, unsigned char *defs);
diff --git a/sm5/ataprint.c b/sm5/ataprint.c
index f63ad77eb..fc04a0e36 100644
--- a/sm5/ataprint.c
+++ b/sm5/ataprint.c
@@ -33,7 +33,7 @@
 #include "extern.h"
 #include "utility.h"
 
-const char *ataprint_c_cvsid="$Id: ataprint.c,v 1.58 2003/02/24 15:51:32 ballen4705 Exp $"
+const char *ataprint_c_cvsid="$Id: ataprint.c,v 1.59 2003/03/06 06:28:47 ballen4705 Exp $"
 ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
@@ -452,9 +452,9 @@ void PrintSmartExtendedSelfTestPollingTime ( struct ata_smart_values *data)
 void PrintSmartAttribWithThres (struct ata_smart_values *data, 
 				struct ata_smart_thresholds *thresholds,
 				int onlyfailed){
-  int i,j;
-  long long rawvalue;
+  int i;
   int needheader=1;
+  char rawstring[64];
     
   // step through all vendor attributes
   for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
@@ -505,55 +505,10 @@ void PrintSmartAttribWithThres (struct ata_smart_values *data,
       pout("0x%04x   %.3d   %.3d   %.3d    %-9s%-12s", 
 	     (int)disk->status.all, (int)disk->current, (int)disk->worst,
 	     (int)thre->threshold, type, status);
-      
-      // convert the six individual bytes to a long long (8 byte) integer
-      rawvalue = 0;
-      for (j=0; j<6; j++) {
-	// This looks a bit roundabout, but is necessary.  Don't
-	// succumb to the temptation to use raw[j]<<(8*j) since under
-	// the normal rules this will be promoted to the native type.
-	// On a 32 bit machine this might then overflow.
-	long long temp;
-	temp = disk->raw[j];
-	temp <<= 8*j;
-	rawvalue |= temp;
-      }
 
-      // This switch statement is where we handle Raw attributes
-      // that are stored in an unusual vendor-specific format,
-      switch (disk->id){
-	// Power on time
-      case 9:
-	if (con->attributedefs[9]==1){
-	  // minutes
-	  long long tmp1=rawvalue/60;
-	  long long tmp2=rawvalue%60;
-	  pout("%lluh+%02llum\n", tmp1, tmp2);
-	}
-	else if (con->attributedefs[9]==3){
-	  // seconds
-	  long long hours=rawvalue/3600;
-	  long long minutes=(rawvalue-3600*hours)/60;
-	  long long seconds=rawvalue%60;
-	  pout("%lluh+%02llum+%02llus\n", hours, minutes, seconds);
-	}
-	else
-	  // hours
-	  pout("%llu\n", rawvalue);  //stored in hours
-	break;
-	// Temperature
-      case 194:
-	pout("%d", (int)disk->raw[0]);
-	if (rawvalue==disk->raw[0])
-	  pout("\n");
-	else
-	  // The other bytes are in use. Try IBM's model
-	  pout(" (Lifetime Min/Max %d/%d)\n",(int)disk->raw[2],
-		 (int)disk->raw[4]);
-	break;
-      default:
-	pout("%llu\n", rawvalue);
-      }
+      // print raw value of attribute
+      ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs);
+      pout("%s\n", rawstring);
       
       // print a warning if there is inconsistency here!
       if (disk->id != thre->id){
@@ -568,7 +523,6 @@ void PrintSmartAttribWithThres (struct ata_smart_values *data,
   if (!needheader) pout("\n");
 }
 
-
 void ataPrintGeneralSmartValues(struct ata_smart_values *data){
   pout("General SMART Values:\n");
   
diff --git a/sm5/ataprint.cpp b/sm5/ataprint.cpp
index 8fe8b0ef4..b80ef987b 100644
--- a/sm5/ataprint.cpp
+++ b/sm5/ataprint.cpp
@@ -33,7 +33,7 @@
 #include "extern.h"
 #include "utility.h"
 
-const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.58 2003/02/24 15:51:32 ballen4705 Exp $"
+const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.59 2003/03/06 06:28:47 ballen4705 Exp $"
 ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
@@ -452,9 +452,9 @@ void PrintSmartExtendedSelfTestPollingTime ( struct ata_smart_values *data)
 void PrintSmartAttribWithThres (struct ata_smart_values *data, 
 				struct ata_smart_thresholds *thresholds,
 				int onlyfailed){
-  int i,j;
-  long long rawvalue;
+  int i;
   int needheader=1;
+  char rawstring[64];
     
   // step through all vendor attributes
   for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
@@ -505,55 +505,10 @@ void PrintSmartAttribWithThres (struct ata_smart_values *data,
       pout("0x%04x   %.3d   %.3d   %.3d    %-9s%-12s", 
 	     (int)disk->status.all, (int)disk->current, (int)disk->worst,
 	     (int)thre->threshold, type, status);
-      
-      // convert the six individual bytes to a long long (8 byte) integer
-      rawvalue = 0;
-      for (j=0; j<6; j++) {
-	// This looks a bit roundabout, but is necessary.  Don't
-	// succumb to the temptation to use raw[j]<<(8*j) since under
-	// the normal rules this will be promoted to the native type.
-	// On a 32 bit machine this might then overflow.
-	long long temp;
-	temp = disk->raw[j];
-	temp <<= 8*j;
-	rawvalue |= temp;
-      }
 
-      // This switch statement is where we handle Raw attributes
-      // that are stored in an unusual vendor-specific format,
-      switch (disk->id){
-	// Power on time
-      case 9:
-	if (con->attributedefs[9]==1){
-	  // minutes
-	  long long tmp1=rawvalue/60;
-	  long long tmp2=rawvalue%60;
-	  pout("%lluh+%02llum\n", tmp1, tmp2);
-	}
-	else if (con->attributedefs[9]==3){
-	  // seconds
-	  long long hours=rawvalue/3600;
-	  long long minutes=(rawvalue-3600*hours)/60;
-	  long long seconds=rawvalue%60;
-	  pout("%lluh+%02llum+%02llus\n", hours, minutes, seconds);
-	}
-	else
-	  // hours
-	  pout("%llu\n", rawvalue);  //stored in hours
-	break;
-	// Temperature
-      case 194:
-	pout("%d", (int)disk->raw[0]);
-	if (rawvalue==disk->raw[0])
-	  pout("\n");
-	else
-	  // The other bytes are in use. Try IBM's model
-	  pout(" (Lifetime Min/Max %d/%d)\n",(int)disk->raw[2],
-		 (int)disk->raw[4]);
-	break;
-      default:
-	pout("%llu\n", rawvalue);
-      }
+      // print raw value of attribute
+      ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs);
+      pout("%s\n", rawstring);
       
       // print a warning if there is inconsistency here!
       if (disk->id != thre->id){
@@ -568,7 +523,6 @@ void PrintSmartAttribWithThres (struct ata_smart_values *data,
   if (!needheader) pout("\n");
 }
 
-
 void ataPrintGeneralSmartValues(struct ata_smart_values *data){
   pout("General SMART Values:\n");
   
diff --git a/sm5/smartd.8 b/sm5/smartd.8
index 2c510f7fc..60c72bf63 100644
--- a/sm5/smartd.8
+++ b/sm5/smartd.8
@@ -1,6 +1,6 @@
 \# Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 \# 
-\# $Id: smartd.8,v 1.67 2003/02/27 23:19:16 ballen4705 Exp $
+\# $Id: smartd.8,v 1.68 2003/03/06 06:28:47 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
@@ -16,7 +16,7 @@
 \# Research Center), Jack Baskin School of Engineering, University of
 \# California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 \#
-.TH SMARTD 8  "$Date: 2003/02/27 23:19:16 $" "smartmontools-5.1"
+.TH SMARTD 8  "$Date: 2003/03/06 06:28:47 $" "smartmontools-5.1"
 .SH NAME
 smartd \- S.M.A.R.T. Daemon
 .SH SYNOPSIS
@@ -680,7 +680,7 @@ command-line option.]
 .TP
 .B \-u
 Report anytime that a Usage Attribute has changed its value
-since the last check, N seconds ago. [Please see the
+since the last check, 30 minutes ago. [Please see the
 .B smartctl \-A
 command-line option.]
 .TP
@@ -688,7 +688,7 @@ command-line option.]
 Equivalent to turning on the two previous flags '\-p' and '\-u'.
 Tracks changes in
 .I all
-device Attributes. [Please see the
+device Attributes (both Prefailure and Usage). [Please see the
 .B smartctl \-A
 command-line option.]
 .TP
@@ -697,8 +697,9 @@ Ignore device Attribute number
 .B ID
 when checking for failure of Usage Attributes.
 .B ID
-must be a decimal integer in the range from 1 to 255.  This Directive modifies
-the behavior of the '\-f' Directive and has no effect without it.  
+must be a decimal integer in the range from 1 to 255.  This Directive
+modifies the behavior of the '\-f' Directive and has no effect without
+it.
 
 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)
@@ -711,13 +712,57 @@ Ignore device Attribute
 when tracking changes in the Attribute values.
 .B ID
 must be a decimal integer in the range from 1 to 255.  This Directive modifies
-the behavior of the '\-p', '\-u', and '\-t' Directives and has no effect
-without one of them.  
+the behavior of the '\-p', '\-u', and '\-t' tracking Directives and has no effect
+without one of them.
 
 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.  This Directive may appear multiple
 times for a single device, if you want to ignore multiple Attributes.
+.TP
+.B \-r ID
+When tracking, report the
+.I Raw
+value of Attribute
+.B ID
+along with its (normally reported)
+.I Normalized
+value.
+.B ID
+must be a decimal integer in the range from 1 to 255.  This Directive modifies
+the behavior of the '\-p', '\-u', and '\-t' tracking Directives and has no effect
+without one of them.  This Directive may be given multiple times.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).
+
+.TP
+.B \-R ID
+When tracking,
+report whenever the
+.I Raw
+value of Attribute
+.B ID
+changes.  (Normally
+.B smartd
+only tracks/reports changes of the
+.I Normalized
+Attribute values.)
+.B ID
+must be a decimal integer in the range from 1 to 255.  This Directive
+modifies the behavior of the '\-p', '\-u', and '\-t' tracking Directives and
+has no effect without one of them.  This Directive may be given
+multiple times.
+
+If this Directive is given, it automatically implies the '\-r'
+Directive for the same Attribute, so that the Raw value of the
+Attribute is reported.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).  It is also useful for understanding how
+different types of system behavior affects the values of certain
+Attributes.
+
 .TP
 .B \-v N,OPTION
 Modifies the labeling for Attribute N, for disks which use
@@ -860,6 +905,11 @@ then powers down the machine.
 Some example scripts are distributed with the smartmontools package,
 in /usr/share/doc/smartmontools-5.1/examplescripts/.
 
+Please note that these scripts typically run as root, so any files
+that they read/write should not be writable by ordinary users or
+reside in directories like /tmp that are writable by ordinary users
+and may expose your system to symlink attacks.
+
 \# ENDINCLUDE
 \# DO NOT MODIFY THIS OR PREVIOUS/NEXT LINES. THIS DEFINES THE 
 \# END OF THE INCLUDE SECTION FOR smartd.conf.5
@@ -977,4 +1027,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.67 2003/02/27 23:19:16 ballen4705 Exp $
+$Id: smartd.8,v 1.68 2003/03/06 06:28:47 ballen4705 Exp $
diff --git a/sm5/smartd.c b/sm5/smartd.c
index bdca48d3b..1a9cdd7f1 100644
--- a/sm5/smartd.c
+++ b/sm5/smartd.c
@@ -50,7 +50,7 @@
 
 // CVS ID strings
 extern const char *atacmds_c_cvsid, *ataprint_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
-const char *smartd_c_cvsid="$Id: smartd.c,v 1.110 2003/02/09 21:12:33 ballen4705 Exp $" 
+const char *smartd_c_cvsid="$Id: smartd.c,v 1.111 2003/03/06 06:28:47 ballen4705 Exp $" 
 ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID UTILITY_H_CVSID; 
 
 // global variable used for control of printing, passing arguments, etc.
@@ -378,6 +378,8 @@ void Directives() {
   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,"  -r ID   Also report Raw values of Attribute ID with -p, -u or -t\n");
+  printout(LOG_INFO,"  -R ID   Track changes in Attribute ID Raw value with -p, -u or -t\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,"  -v N,ST Modifies labeling of Attribute N (see man page)  \n");
@@ -700,22 +702,17 @@ int scsidevicescan(scsidevices_t *devices, cfgfile *cfg){
 }
 
 // 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)
-
-//  [00000000x][attribute ID][old value][new value]
-int  ataCompareSmartValues2(struct ata_smart_values *new,
+// is NOT the attribute ID number.. If (Normalized & Raw) equal,
+// then return 0, else nonzero.
+int  ataCompareSmartValues(changedattribute_t *delta,
+			    struct ata_smart_values *new,
 			    struct ata_smart_values *old,
 			    struct ata_smart_thresholds *thresholds,
 			    int n, char *name){
   struct ata_smart_attribute *now,*was;
   struct ata_smart_threshold_entry *thre;
   unsigned char oldval,newval;
-  int returnvalue;
+  int sameraw;
 
   // check that attribute number in range, and no null pointers
   if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thresholds)
@@ -738,22 +735,29 @@ int  ataCompareSmartValues2(struct ata_smart_values *new,
     return 0;
   }
 
-  // if values have not changed, return
+  // new and old values of Normalized Attributes
   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)
+  // See if the RAW values are unchanged (ie, the same)
+  if (memcmp(now->raw, was->raw, 6))
+    sameraw=0;
+  else
+    sameraw=1;
+  
+  // if any values out of the allowed range, or if the values haven't
+  // changed, return 0
+  if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw))
     return 0;
   
-  // values have changed.  Construct output
-  returnvalue=0;
-  returnvalue |= newval;
-  returnvalue |= oldval<<8;
-  returnvalue |= now->id<<16;
-  returnvalue |= (now->status.flag.prefailure)<<24;
+  // values have changed.  Construct output and return
+  delta->newval=newval;
+  delta->oldval=oldval;
+  delta->id=now->id;
+  delta->prefail=now->status.flag.prefailure;
+  delta->sameraw=sameraw;
 
-  return returnvalue;
+  return 1;
 }
 
 // This looks to see if the corresponding bit of the 32 bytes is set.
@@ -818,15 +822,17 @@ int ataCheckDevice(atadevices_t *drive){
       // look for failed usage attributes, or track usage or prefail attributes
       for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
 	int att;
-	
+	changedattribute_t delta;
+
+
 	// 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?
+	  // are we ignoring failures of this attribute?
 	  att *= -1;
-	  if (!isattoff(att, cfg->failatt, 0)){
+	  if (!isattoff(att, cfg->monitorattflags, 0)){
 	    char attname[64], *loc=attname;
 	    
 	    // get attribute name & skip white space
@@ -839,36 +845,46 @@ int ataCheckDevice(atadevices_t *drive){
 	  }
 	}
 	
-	// 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, name)))){
-
-	  // I should probably clean this up by defining a union to
-	  // with one int=four unsigned chars to do this.
-	  const int mask=0xff;
-	  int newval =(att>>0)  & mask;
-	  int oldval =(att>>8)  & mask;
-	  int id     =(att>>16) & mask;
-	  int prefail=(att>>24) & mask;
-
-	  // for printing attribute name
-	  char attname[64],*loc=attname;
-	  
+	// This block tracks usage or prefailure attributes to see if
+	// they are changing.  It also looks for changes in RAW values
+	// if this has been requested by user.
+	if ((cfg->usage || cfg->prefail) && ataCompareSmartValues(&delta, &curval, drive->smartval, thresh, i, name)){
+	  unsigned char id=delta.id;
+
+	  // if the only change is the raw value, and we're not
+	  // tracking raw value, then continue loop over attributes
+	  if (!delta.sameraw && delta.newval==delta.oldval && !isattoff(id, cfg->monitorattflags+96, 0))
+	    continue;
+
 	  // are we tracking this attribute?
-	  if (!isattoff(id, cfg->trackatt, 0)){
-	    
+	  if (!isattoff(id, cfg->monitorattflags+32, 0)){
+	    char newrawstring[64], oldrawstring[64], attname[64], *loc=attname;
+
 	    // get attribute name, skip spaces
 	    ataPrintSmartAttribName(loc, id, con->attributedefs);
 	    while (*loc && *loc==' ') loc++;
 	    
+	    // has the user asked for us to print raw values?
+	    if (isattoff(id, cfg->monitorattflags+64, 0)) {
+	      // get raw values (as a string) and add to printout
+	      char rawstring[64];
+	      ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, con->attributedefs);
+	      sprintf(newrawstring, " [Raw %s]", rawstring);
+	      ataPrintSmartAttribRawValue(rawstring, drive->smartval->vendor_attributes+i, con->attributedefs);
+	      sprintf(oldrawstring, " [Raw %s]", rawstring);
+	    }
+	    else
+	      newrawstring[0]=oldrawstring[0]='\0';
+
 	    // prefailure attribute
-	    if (cfg->prefail && prefail)
-	      printout(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d to %d\n",
-		       name, loc, (int)oldval, (int)newval);
+	    if (cfg->prefail && delta.prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n",
+		       name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
 
 	    // usage attribute
-	    if (cfg->usage && !prefail)
-	      printout(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d to %d\n",
-		       name, loc, (int)oldval, (int)newval);
+	    if (cfg->usage && !delta.prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n",
+		       name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
 	  }
 	} // endof block tracking usage or prefailure
       } // end of loop over attributes
@@ -1231,12 +1247,23 @@ int parsetoken(char *token,cfgfile *cfg){
   case 'i':
     // ignore failure of usage attribute
     val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
-    isattoff(val,cfg->failatt,1);
+    isattoff(val,cfg->monitorattflags,1);
     break;
   case 'I':
     // ignore attribute for tracking purposes
     val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
-    isattoff(val,cfg->trackatt,1);
+    isattoff(val,cfg->monitorattflags+32,1);
+    break;
+  case 'r':
+    // print raw value when tracking
+    val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
+    isattoff(val,cfg->monitorattflags+64,1);
+    break;
+  case 'R':
+    // track changes in raw value (forces printing of raw value)
+    val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
+    isattoff(val,cfg->monitorattflags+64,1);
+    isattoff(val,cfg->monitorattflags+96,1);
     break;
   case 'm':
     // send email to address that follows
@@ -1331,13 +1358,12 @@ int parseconfigline(int entry, int lineno,char *line){
   // bit per possible attribute ID.  See isattoff()
   cfg->name=strdup(name);
   if (!devscan){
-    cfg->failatt=(unsigned char *)calloc(32,1);
-    cfg->trackatt=(unsigned char *)calloc(32,1);
+    cfg->monitorattflags=(unsigned char *)calloc(NMONITOR*32,1);
     cfg->attributedefs=(unsigned char *)calloc(256,1);
   }
 
   // check that all memory allocations were sucessful
-  if (!cfg->name || (!devscan && (!cfg->failatt || !cfg->trackatt || !cfg->attributedefs))) {
+  if (!cfg->name || (!devscan && (!cfg->monitorattflags || !cfg->attributedefs))) {
     printout(LOG_INFO,"No memory to store file: %s line %d, %s\n", CONFIGFILE, lineno, strerror(errno));
     exit(1);
   }
@@ -1693,10 +1719,9 @@ int makeconfigentries(int num, char *name, int isata, int start, int scandirecti
     
     // put in the device name
     cfg->name=strdup(name);
-    cfg->failatt=(unsigned char *)calloc(32,1);
-    cfg->trackatt=(unsigned char *)calloc(32,1);
+    cfg->monitorattflags=(unsigned char *)calloc(NMONITOR*32,1);
     cfg->attributedefs=(unsigned char *)calloc(256,1);
-    if (!cfg->name || !cfg->failatt || !cfg->trackatt || !cfg->attributedefs) {
+    if (!cfg->name || !cfg->monitorattflags || !cfg->attributedefs) {
       printout(LOG_INFO,"No memory for %d'th device after %s, %s\n", i, name, strerror(errno));
       exit(1);
     }
diff --git a/sm5/smartd.conf b/sm5/smartd.conf
index 1cfac908e..2790af6d5 100644
--- a/sm5/smartd.conf
+++ b/sm5/smartd.conf
@@ -44,9 +44,11 @@ DEVICESCAN
 #   -f      Monitor for failure of any 'Usage' Attributes
 #   -m ADD  Send warning email to ADD for -H, -l error, -l selftest, and -f
 #   -M TYPE Modify email warning behavior (see man page)
-#   -p      Report changes in 'Prefailure' Attributes
-#   -u      Report changes in 'Usage' Attributes
+#   -p      Report changes in 'Prefailure' Normalized Attributes
+#   -u      Report changes in 'Usage' Normalized Attributes
 #   -t      Equivalent to -p and -u Directives
+#   -r ID   Also report Raw values of Attribute ID with -p, -u or -t
+#   -R ID   Track changes in Attribute ID Raw value with -p, -u or -t
 #   -i ID   Ignore Attribute ID for -f Directive
 #   -I ID   Ignore Attribute ID for -p, -u or -t Directive
 #   -v N,ST Modifies labeling of Attribute N (see man page)
diff --git a/sm5/smartd.conf.5 b/sm5/smartd.conf.5
index 56cabcbf0..7012bff98 100644
--- a/sm5/smartd.conf.5
+++ b/sm5/smartd.conf.5
@@ -1,6 +1,6 @@
 \# Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 \# 
-\# $Id: smartd.conf.5,v 1.31 2003/02/24 15:51:39 ballen4705 Exp $
+\# $Id: smartd.conf.5,v 1.32 2003/03/06 06:28:47 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
@@ -16,7 +16,7 @@
 \# Research Center), Jack Baskin School of Engineering, University of
 \# California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 \#
-.TH SMARTD.CONF 5  "$Date: 2003/02/24 15:51:39 $" "smartmontools-5.1"
+.TH SMARTD.CONF 5  "$Date: 2003/03/06 06:28:47 $" "smartmontools-5.1"
 .SH NAME
 smartd.conf \- S.M.A.R.T. Monitoring Daemon Configuration File
 
@@ -478,7 +478,7 @@ command-line option.]
 .TP
 .B \-u
 Report anytime that a Usage Attribute has changed its value
-since the last check, N seconds ago. [Please see the
+since the last check, 30 minutes ago. [Please see the
 .B smartctl \-A
 command-line option.]
 .TP
@@ -486,7 +486,7 @@ command-line option.]
 Equivalent to turning on the two previous flags '\-p' and '\-u'.
 Tracks changes in
 .I all
-device Attributes. [Please see the
+device Attributes (both Prefailure and Usage). [Please see the
 .B smartctl \-A
 command-line option.]
 .TP
@@ -495,8 +495,9 @@ Ignore device Attribute number
 .B ID
 when checking for failure of Usage Attributes.
 .B ID
-must be a decimal integer in the range from 1 to 255.  This Directive modifies
-the behavior of the '\-f' Directive and has no effect without it.  
+must be a decimal integer in the range from 1 to 255.  This Directive
+modifies the behavior of the '\-f' Directive and has no effect without
+it.
 
 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)
@@ -509,13 +510,57 @@ Ignore device Attribute
 when tracking changes in the Attribute values.
 .B ID
 must be a decimal integer in the range from 1 to 255.  This Directive modifies
-the behavior of the '\-p', '\-u', and '\-t' Directives and has no effect
-without one of them.  
+the behavior of the '\-p', '\-u', and '\-t' tracking Directives and has no effect
+without one of them.
 
 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.  This Directive may appear multiple
 times for a single device, if you want to ignore multiple Attributes.
+.TP
+.B \-r ID
+When tracking, report the
+.I Raw
+value of Attribute
+.B ID
+along with its (normally reported)
+.I Normalized
+value.
+.B ID
+must be a decimal integer in the range from 1 to 255.  This Directive modifies
+the behavior of the '\-p', '\-u', and '\-t' tracking Directives and has no effect
+without one of them.  This Directive may be given multiple times.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).
+
+.TP
+.B \-R ID
+When tracking,
+report whenever the
+.I Raw
+value of Attribute
+.B ID
+changes.  (Normally
+.B smartd
+only tracks/reports changes of the
+.I Normalized
+Attribute values.)
+.B ID
+must be a decimal integer in the range from 1 to 255.  This Directive
+modifies the behavior of the '\-p', '\-u', and '\-t' tracking Directives and
+has no effect without one of them.  This Directive may be given
+multiple times.
+
+If this Directive is given, it automatically implies the '\-r'
+Directive for the same Attribute, so that the Raw value of the
+Attribute is reported.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).  It is also useful for understanding how
+different types of system behavior affects the values of certain
+Attributes.
+
 .TP
 .B \-v N,OPTION
 Modifies the labeling for Attribute N, for disks which use
@@ -658,6 +703,11 @@ then powers down the machine.
 Some example scripts are distributed with the smartmontools package,
 in /usr/share/doc/smartmontools-5.1/examplescripts/.
 
+Please note that these scripts typically run as root, so any files
+that they read/write should not be writable by ordinary users or
+reside in directories like /tmp that are writable by ordinary users
+and may expose your system to symlink attacks.
+
 \# ENDINCLUDE
 \# DO NOT MODIFY THIS OR PREVIOUS/NEXT LINES. THIS DEFINES THE 
 \# END OF THE INCLUDED SECTION FROM smartd.8
@@ -701,4 +751,4 @@ SEE ALSO:
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartd.conf.5,v 1.31 2003/02/24 15:51:39 ballen4705 Exp $
+$Id: smartd.conf.5,v 1.32 2003/03/06 06:28:47 ballen4705 Exp $
diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp
index edf101f12..3ba93a940 100644
--- a/sm5/smartd.cpp
+++ b/sm5/smartd.cpp
@@ -50,7 +50,7 @@
 
 // CVS ID strings
 extern const char *atacmds_c_cvsid, *ataprint_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
-const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.110 2003/02/09 21:12:33 ballen4705 Exp $" 
+const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.111 2003/03/06 06:28:47 ballen4705 Exp $" 
 ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID UTILITY_H_CVSID; 
 
 // global variable used for control of printing, passing arguments, etc.
@@ -378,6 +378,8 @@ void Directives() {
   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,"  -r ID   Also report Raw values of Attribute ID with -p, -u or -t\n");
+  printout(LOG_INFO,"  -R ID   Track changes in Attribute ID Raw value with -p, -u or -t\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,"  -v N,ST Modifies labeling of Attribute N (see man page)  \n");
@@ -700,22 +702,17 @@ int scsidevicescan(scsidevices_t *devices, cfgfile *cfg){
 }
 
 // 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)
-
-//  [00000000x][attribute ID][old value][new value]
-int  ataCompareSmartValues2(struct ata_smart_values *new,
+// is NOT the attribute ID number.. If (Normalized & Raw) equal,
+// then return 0, else nonzero.
+int  ataCompareSmartValues(changedattribute_t *delta,
+			    struct ata_smart_values *new,
 			    struct ata_smart_values *old,
 			    struct ata_smart_thresholds *thresholds,
 			    int n, char *name){
   struct ata_smart_attribute *now,*was;
   struct ata_smart_threshold_entry *thre;
   unsigned char oldval,newval;
-  int returnvalue;
+  int sameraw;
 
   // check that attribute number in range, and no null pointers
   if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thresholds)
@@ -738,22 +735,29 @@ int  ataCompareSmartValues2(struct ata_smart_values *new,
     return 0;
   }
 
-  // if values have not changed, return
+  // new and old values of Normalized Attributes
   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)
+  // See if the RAW values are unchanged (ie, the same)
+  if (memcmp(now->raw, was->raw, 6))
+    sameraw=0;
+  else
+    sameraw=1;
+  
+  // if any values out of the allowed range, or if the values haven't
+  // changed, return 0
+  if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw))
     return 0;
   
-  // values have changed.  Construct output
-  returnvalue=0;
-  returnvalue |= newval;
-  returnvalue |= oldval<<8;
-  returnvalue |= now->id<<16;
-  returnvalue |= (now->status.flag.prefailure)<<24;
+  // values have changed.  Construct output and return
+  delta->newval=newval;
+  delta->oldval=oldval;
+  delta->id=now->id;
+  delta->prefail=now->status.flag.prefailure;
+  delta->sameraw=sameraw;
 
-  return returnvalue;
+  return 1;
 }
 
 // This looks to see if the corresponding bit of the 32 bytes is set.
@@ -818,15 +822,17 @@ int ataCheckDevice(atadevices_t *drive){
       // look for failed usage attributes, or track usage or prefail attributes
       for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
 	int att;
-	
+	changedattribute_t delta;
+
+
 	// 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?
+	  // are we ignoring failures of this attribute?
 	  att *= -1;
-	  if (!isattoff(att, cfg->failatt, 0)){
+	  if (!isattoff(att, cfg->monitorattflags, 0)){
 	    char attname[64], *loc=attname;
 	    
 	    // get attribute name & skip white space
@@ -839,36 +845,46 @@ int ataCheckDevice(atadevices_t *drive){
 	  }
 	}
 	
-	// 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, name)))){
-
-	  // I should probably clean this up by defining a union to
-	  // with one int=four unsigned chars to do this.
-	  const int mask=0xff;
-	  int newval =(att>>0)  & mask;
-	  int oldval =(att>>8)  & mask;
-	  int id     =(att>>16) & mask;
-	  int prefail=(att>>24) & mask;
-
-	  // for printing attribute name
-	  char attname[64],*loc=attname;
-	  
+	// This block tracks usage or prefailure attributes to see if
+	// they are changing.  It also looks for changes in RAW values
+	// if this has been requested by user.
+	if ((cfg->usage || cfg->prefail) && ataCompareSmartValues(&delta, &curval, drive->smartval, thresh, i, name)){
+	  unsigned char id=delta.id;
+
+	  // if the only change is the raw value, and we're not
+	  // tracking raw value, then continue loop over attributes
+	  if (!delta.sameraw && delta.newval==delta.oldval && !isattoff(id, cfg->monitorattflags+96, 0))
+	    continue;
+
 	  // are we tracking this attribute?
-	  if (!isattoff(id, cfg->trackatt, 0)){
-	    
+	  if (!isattoff(id, cfg->monitorattflags+32, 0)){
+	    char newrawstring[64], oldrawstring[64], attname[64], *loc=attname;
+
 	    // get attribute name, skip spaces
 	    ataPrintSmartAttribName(loc, id, con->attributedefs);
 	    while (*loc && *loc==' ') loc++;
 	    
+	    // has the user asked for us to print raw values?
+	    if (isattoff(id, cfg->monitorattflags+64, 0)) {
+	      // get raw values (as a string) and add to printout
+	      char rawstring[64];
+	      ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, con->attributedefs);
+	      sprintf(newrawstring, " [Raw %s]", rawstring);
+	      ataPrintSmartAttribRawValue(rawstring, drive->smartval->vendor_attributes+i, con->attributedefs);
+	      sprintf(oldrawstring, " [Raw %s]", rawstring);
+	    }
+	    else
+	      newrawstring[0]=oldrawstring[0]='\0';
+
 	    // prefailure attribute
-	    if (cfg->prefail && prefail)
-	      printout(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d to %d\n",
-		       name, loc, (int)oldval, (int)newval);
+	    if (cfg->prefail && delta.prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n",
+		       name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
 
 	    // usage attribute
-	    if (cfg->usage && !prefail)
-	      printout(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d to %d\n",
-		       name, loc, (int)oldval, (int)newval);
+	    if (cfg->usage && !delta.prefail)
+	      printout(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n",
+		       name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
 	  }
 	} // endof block tracking usage or prefailure
       } // end of loop over attributes
@@ -1231,12 +1247,23 @@ int parsetoken(char *token,cfgfile *cfg){
   case 'i':
     // ignore failure of usage attribute
     val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
-    isattoff(val,cfg->failatt,1);
+    isattoff(val,cfg->monitorattflags,1);
     break;
   case 'I':
     // ignore attribute for tracking purposes
     val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
-    isattoff(val,cfg->trackatt,1);
+    isattoff(val,cfg->monitorattflags+32,1);
+    break;
+  case 'r':
+    // print raw value when tracking
+    val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
+    isattoff(val,cfg->monitorattflags+64,1);
+    break;
+  case 'R':
+    // track changes in raw value (forces printing of raw value)
+    val=inttoken(arg=strtok(NULL,delim), name, token, lineno, CONFIGFILE, 1, 255);
+    isattoff(val,cfg->monitorattflags+64,1);
+    isattoff(val,cfg->monitorattflags+96,1);
     break;
   case 'm':
     // send email to address that follows
@@ -1331,13 +1358,12 @@ int parseconfigline(int entry, int lineno,char *line){
   // bit per possible attribute ID.  See isattoff()
   cfg->name=strdup(name);
   if (!devscan){
-    cfg->failatt=(unsigned char *)calloc(32,1);
-    cfg->trackatt=(unsigned char *)calloc(32,1);
+    cfg->monitorattflags=(unsigned char *)calloc(NMONITOR*32,1);
     cfg->attributedefs=(unsigned char *)calloc(256,1);
   }
 
   // check that all memory allocations were sucessful
-  if (!cfg->name || (!devscan && (!cfg->failatt || !cfg->trackatt || !cfg->attributedefs))) {
+  if (!cfg->name || (!devscan && (!cfg->monitorattflags || !cfg->attributedefs))) {
     printout(LOG_INFO,"No memory to store file: %s line %d, %s\n", CONFIGFILE, lineno, strerror(errno));
     exit(1);
   }
@@ -1693,10 +1719,9 @@ int makeconfigentries(int num, char *name, int isata, int start, int scandirecti
     
     // put in the device name
     cfg->name=strdup(name);
-    cfg->failatt=(unsigned char *)calloc(32,1);
-    cfg->trackatt=(unsigned char *)calloc(32,1);
+    cfg->monitorattflags=(unsigned char *)calloc(NMONITOR*32,1);
     cfg->attributedefs=(unsigned char *)calloc(256,1);
-    if (!cfg->name || !cfg->failatt || !cfg->trackatt || !cfg->attributedefs) {
+    if (!cfg->name || !cfg->monitorattflags || !cfg->attributedefs) {
       printout(LOG_INFO,"No memory for %d'th device after %s, %s\n", i, name, strerror(errno));
       exit(1);
     }
diff --git a/sm5/smartd.h b/sm5/smartd.h
index 358e3f65b..06190d331 100644
--- a/sm5/smartd.h
+++ b/sm5/smartd.h
@@ -23,7 +23,7 @@
  */
 
 #ifndef SMARTD_H_CVSID
-#define SMARTD_H_CVSID "$Id: smartd.h,v 1.28 2003/01/31 03:45:25 ballen4705 Exp $\n"
+#define SMARTD_H_CVSID "$Id: smartd.h,v 1.29 2003/03/06 06:28:47 ballen4705 Exp $\n"
 #endif
 
 // Configuration file
@@ -54,6 +54,10 @@
 #define TRUE 0x01
 #define FALSE 0x00
 
+// Number of monitoring flags per Attribute.  See monitorattflags
+// below.
+#define NMONITOR 4
+
 
 // If user has requested email warning messages, then this structure
 // stores the information about them.
@@ -108,12 +112,14 @@ typedef struct configfile_s {
   // atadevices_t structure.
   unsigned char selflogcount;
   int  ataerrorcount;
-  // following two items point to 32 bytes, in the form of
+  // following NMONITOR items each point to 32 bytes, in the form of
   // 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;
+  // monitorattflags+0  set means ignore failure if it's a usage attribute
+  // monitorattflats+32 set means don't track attribute
+  // monitorattflags+64 set means print raw value when tracking
+  // monitorattflags+96 set means track changes in raw value
+  unsigned char *monitorattflags;
   // See the end of extern.h for a definition of the array of 256
   // bytes that this points to.
   unsigned char *attributedefs;
@@ -139,6 +145,14 @@ typedef struct scsidevices_s {
 } scsidevices_t;
 
 
+typedef struct changedattribute_s {
+  unsigned char newval;
+  unsigned char oldval;
+  unsigned char id;
+  unsigned char prefail;
+  unsigned char sameraw;
+} changedattribute_t;
+
 // Declare our own printing functions...
 void printout(int priority,char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
 void printandmail(cfgfile *cfg, int which, int priority, char *fmt, ...) __attribute__ ((format(printf, 4, 5)));   
diff --git a/sm5/smartmontools.spec b/sm5/smartmontools.spec
index a7eb6b2a0..42dd0417c 100644
--- a/sm5/smartmontools.spec
+++ b/sm5/smartmontools.spec
@@ -1,4 +1,4 @@
-Release:  7
+Release:  8
 Summary:	SMARTmontools - for monitoring S.M.A.R.T. disks and devices
 Summary(cs):	SMARTmontools - pro monitorov�n� S.M.A.R.T. disk� a za��zen�
 Summary(de):	SMARTmontools - zur �berwachung von S.M.A.R.T.-Platten und-Ger�ten
@@ -30,7 +30,7 @@ Packager:       Bruce Allen <smartmontools-support@lists.sourceforge.net>
 # http://ftp1.sourceforge.net/smartmontools/smartmontools-%{version}-%{release}.tar.gz
 
 # CVS ID of this file is:
-# $Id: smartmontools.spec,v 1.90 2003/02/18 21:23:22 ballen4705 Exp $
+# $Id: smartmontools.spec,v 1.91 2003/03/06 06:28:47 ballen4705 Exp $
 
 # Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 # Home page: http://smartmontools.sourceforge.net/
-- 
GitLab