From e7e94309b378079f71bcd3a2206c9f84a698d739 Mon Sep 17 00:00:00 2001
From: ballen4705 <ballen4705@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Sat, 26 Oct 2002 09:24:26 +0000
Subject: [PATCH]     modified length of device name string in smartd internal
 structure     to accomodate max length device name strings

    removed un-implemented (-e = Email notification) option from
    command line arg list.  We'll put it back on when implemeneted.

    smartd now logs serious (fatal) conditions in its operation at
    loglevel LOG_CRIT rather than LOG_INFO before exiting with error.

    smartd used to open a file descriptor for each SMART enabled
    device, and then keep it open the entire time smartd was running.
    This meant that some commands, like IOREADBLKPART did not work,
    since the fd to the device was open.  smartd now opens the device
    when it needs to read values, then closes it.  Also, if one time
    around it can't open the device, it simply prints a warning
    message but does not give up.

    smartd now opens SCSI devices as well using O_RDONLY rather than
    O_RDWR.  If someone can no longer monitor a SCSI device that used
    to be readable, this may well be the reason why.

    smartd never checked if the number of ata or scsi devices detected
    was greater than the max number it could monitor.  Now it does.


git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@137 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/CHANGELOG  |  33 ++++++-
 sm5/TODO       |  37 +++++---
 sm5/VERSION    |   2 +-
 sm5/smartd.c   | 252 ++++++++++++++++++++++++++++++-------------------
 sm5/smartd.cpp | 252 ++++++++++++++++++++++++++++++-------------------
 sm5/smartd.h   |  34 +++----
 6 files changed, 379 insertions(+), 231 deletions(-)

diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index 372442873..d3ddd3bd0 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.22 2002/10/25 17:06:17 ballen4705 Exp $
+$Id: CHANGELOG,v 1.23 2002/10/26 09:24:26 ballen4705 Exp $
 
 Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
@@ -21,13 +21,38 @@ Research Center), Jack Baskin School of Engineering, University of
 California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 
 
-NOTES FOR NEXT RELEASE:
+NOTES FOR FUTURE RELEASES: see TODO file.
 
-    Next release: handle extended error and self-test logs gracefully.
-    Parse and print attribute flag meanings
 
 CURRENT RELEASE (see VERSION file in this directory):
 smartmontools-5.0-VERSION
+
+    modified length of device name string in smartd internal structure
+    to accomodate max length device name strings
+
+    removed un-implemented (-e = Email notification) option from
+    command line arg list.  We'll put it back on when implemeneted.
+
+    smartd now logs serious (fatal) conditions in its operation at
+    loglevel LOG_CRIT rather than LOG_INFO before exiting with error.
+
+    smartd used to open a file descriptor for each SMART enabled
+    device, and then keep it open the entire time smartd was running.
+    This meant that some commands, like IOREADBLKPART did not work,
+    since the fd to the device was open.  smartd now opens the device
+    when it needs to read values, then closes it.  Also, if one time
+    around it can't open the device, it simply prints a warning
+    message but does not give up.
+
+    smartd now opens SCSI devices as well using O_RDONLY rather than
+    O_RDWR.  If someone can no longer monitor a SCSI device that used
+    to be readable, this may well be the reason why.
+
+    smartd never checked if the number of ata or scsi devices detected
+    was greater than the max number it could monitor.  Now it does.
+
+smartmontools-5.0-16
+
     smartd on startup now looks in the configuration file /etc/smartd.conf for
     a list of devices which to include in its monitoring list.  See man page
     (man smartd) for syntax.
diff --git a/sm5/TODO b/sm5/TODO
index 3a35dba3a..4ad6eeae9 100644
--- a/sm5/TODO
+++ b/sm5/TODO
@@ -4,7 +4,7 @@ Home page of code is: http://smartmontools.sourceforge.net
 
 Copyright (C) 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
-$Id: TODO,v 1.15 2002/10/25 14:23:40 ballen4705 Exp $
+$Id: TODO,v 1.16 2002/10/26 09:24:26 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
@@ -34,29 +34,36 @@ Produce version for ATA/ATAPI-7
 Modifications
 -------------
 
-Change smartd so that it also monitors usage as well as prefail attributes for failure
-or changes.  Make this a command line option
+Handle extended error and self-test logs gracefully.  Can someone tell
+me a disk that supports more than log pages 1 and 6?  I need to get a
+disk so I can test this functionality, when I add it.
 
-Perhaps change smartd to look in /proc/ide and /proc/scsi to see what exists? If something
-doesn't exit then don't try to open it?
+Change smartd so that it also monitors usage as well as prefail
+attributes for failure or changes.  Make this a command line option
 
-Currently smartd looks at attribute values and thresholds, then prints out if there is an
-error condition.  Make it also look at the smart status, if that is enabled and working and
-do that test as well.  Perhaps also see if the number of device errors has changed.
+Perhaps change smartd to look in /proc/ide and /proc/scsi to see what
+exists? If something doesn't exit then don't try to open it?
 
-Change smartd so that it monitors the ATA disk error log, and if the number of errors changes,
-log an entry..
+Currently smartd looks at attribute values and thresholds, then prints
+out if there is an error condition.  Make it also look at the smart
+status, if that is enabled and working and do that test as well.
+Perhaps also see if the number of device errors has changed.
 
-Perhaps modify the -q option (quiet mode) so that it only warns of ATA errors if they have (say)
-taken place in the last 168 hours (week).
+Add optional flags to /etc/smartd.conf, so that certain attributes are
+ignored, or so that all usage attributes are ignored.
+
+Change smartd so that it monitors the ATA disk error log, and if the
+number of errors changes, log an entry..
+
+Perhaps modify the -q option (quiet mode) so that it only warns of ATA
+errors if they have (say) taken place in the last 168 hours (week).
 
 Change input command line from using current command line format
 (getopt) to getopt_long() for long input options.  This will be
 helpful in adding device specific options.
 
-Command line option to specify devices to look for in smartd startup
-
-Print flags meanings in Vendor Attribute list -- not hex value --
+Parse and print more attribute flag meanings (IBM ones, eg performance
+etc).
 
 Fixes
 -----
diff --git a/sm5/VERSION b/sm5/VERSION
index b6a7d89c6..98d9bcb75 100644
--- a/sm5/VERSION
+++ b/sm5/VERSION
@@ -1 +1 @@
-16
+17
diff --git a/sm5/smartd.c b/sm5/smartd.c
index e504e80e5..7944644f2 100644
--- a/sm5/smartd.c
+++ b/sm5/smartd.c
@@ -37,41 +37,11 @@
 #include "scsicmds.h"
 #include "smartd.h"
 
+// CVS ID strings
 extern const char *CVSid1, *CVSid2;
-const char *CVSid3="$Id: smartd.c,v 1.31 2002/10/25 14:54:14 ballen4705 Exp $" 
+const char *CVSid3="$Id: smartd.c,v 1.32 2002/10/26 09:24:26 ballen4705 Exp $" 
 CVSID1 CVSID4 CVSID7;
 
-int daemon_init(void){
-  pid_t pid;
-  int i;  
-
-  if ( (pid = fork()) < 0)
-    // unable to fork!
-    exit(1);
-  else if (pid != 0)
-    // we are the parent process -- exit cleanly
-    exit (0);
-
-  // from here on, we are the child process
-  setsid();
-
-  // close any open file descriptors
-  for (i=getdtablesize();i>=0;--i)
-    close(i);
-
-  // redirect any IO attempts to /dev/null
-  // open stdin
-  i=open("/dev/null",O_RDWR);
-  // stdout
-  dup(i);
-  // stderr
-  dup(i);
-  umask(0);
-  chdir("/");
-  return(0);
-}
-
-
 // This function prints either to stdout or to the syslog as needed
 void printout(int priority,char *fmt, ...){
   va_list ap;
@@ -85,22 +55,54 @@ void printout(int priority,char *fmt, ...){
   return;
 }
 
-// Printing function for atacmds
+// Printing function for debugging atacmds. For debugging set 0 to 1
+// in #if statement
 void pout(char *fmt, ...){
   va_list ap;
+  
   // initialize variable argument list 
   va_start(ap,fmt);
-  va_end(ap);
-  return;
 #if (0)
-  // print out
   vprintf(fmt,ap);
+#endif
   va_end(ap);
   return;
-#endif
 }
 
+// Forks new process, closes all file descriptors, redirects stdin,
+// stdout, stderr
+int daemon_init(void){
+  pid_t pid;
+  int i;  
 
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    printout(LOG_CRIT,"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
+  setsid();
+
+  // close any open file descriptors
+  for (i=getdtablesize();i>=0;--i)
+    close(i);
+  
+  // redirect any IO attempts to /dev/null for stdin
+  i=open("/dev/null",O_RDWR);
+  // stdout
+  dup(i);
+  // stderr
+  dup(i);
+  umask(0);
+  chdir("/");
+  return(0);
+}
+
+// Prints header identifying version of code and home
 void printhead(){
   printout(LOG_INFO,"smartd version %d.%d-%d - S.M.A.R.T. Daemon.\n",
            RELEASE_MAJOR, RELEASE_MINOR, SMARTMONTOOLS_VERSION);
@@ -114,23 +116,45 @@ void Usage (void){
   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,"Configuration file: /etc/smartd.conf\n");
+  printout(LOG_INFO,"Optional configuration file: %s\n",CONFIGFILE);
 }
-	
+
+// returns negative if problem, else fd>=0
+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);
+    else
+      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+    return -1;
+  }
+  // device opened sucessfully
+  return fd;
+}
+
+// returns 1 if problem, else zero
+int closedevice(int fd){
+  if (close(fd)){
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Closing file descriptor %d failed\n",sys_errlist[errno],fd);
+    else
+      printout(LOG_INFO,"Closing file descriptor %d failed\n",fd);
+    return 1;
+  }
+  // device opened sucessfully
+  return 0;
+}
+
 // scan to see what ata devices there are, and if they support SMART
 int atadevicescan (atadevices_t *devices, char *device){
   int fd;
   struct hd_driveid drive;
   
   printout(LOG_INFO,"Opening device %s\n", device);
-  fd = open(device, O_RDONLY);
-  if (fd < 0) {
-    if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
-    else
-      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+  if ((fd=opendevice(device))<0)
+    // device open failed
     return 1;
-  }
   
   if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
     // device exists, but not able to do SMART
@@ -153,7 +177,13 @@ int atadevicescan (atadevices_t *devices, char *device){
     return 4;
   }
   
-  // device exists, and does SMART.  Add to list
+  // Device exists, and does SMART.  Add to list
+  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);
   devices[numatadevices].fd = fd;
   strcpy(devices[numatadevices].devicename, device);
@@ -167,6 +197,7 @@ int atadevicescan (atadevices_t *devices, char *device){
     isSupportSelfTest(devices[numatadevices].smartval);
   
   numatadevices++;
+  closedevice(fd);
   return 0;
 }
 
@@ -177,18 +208,11 @@ int scsidevicescan (scsidevices_t *devices, char *device){
   int i, fd, smartsupport;
   unsigned char  tBuf[4096];
   
-  
-  // open device
   printout(LOG_INFO,"Opening device %s\n", device);
-  fd=open(device, O_RDWR);
-  if (fd<0) {
-    if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
-    else
-      printout(LOG_INFO,"Device: %s, Opening device failed\n", device);
+  if ((fd=opendevice(device))<0)
+    // device open failed
     return 1;
-  }
-
+  
   // check that it's ready for commands
   if (!testunitready(fd)){
     printout(LOG_INFO,"Device: %s, Failed Test Unit Ready\n", device);
@@ -211,6 +235,13 @@ int scsidevicescan (scsidevices_t *devices, char *device){
     return 4;
   }
 
+  // Device exists, and does SMART.  Add to list
+  if (numscsidevices>=MAXSCSIDEVICES){
+    printout(LOG_CRIT,"smartd has found more than MAXSCSIDEVICES=%d SCSI devices.\n"
+	     "Recompile code from " PROJECTHOME " with larger MAXSCSIDEVICES\n",numscsidevices);
+    exit(1);
+  }
+
   // now we can proceed to register the device
   printout(LOG_INFO, "Device: %s, Found and is SMART capable. Adding to \"monitor\" list.\n",device);
   devices[numscsidevices].fd = fd;
@@ -233,6 +264,7 @@ int scsidevicescan (scsidevices_t *devices, char *device){
     }	
   }
   numscsidevices++;
+  closedevice(fd);
   return 0;
 }
 
@@ -269,19 +301,28 @@ void ataCompareSmartValues (atadevices_t *device, struct ata_smart_values new ){
 int ataCheckDevice( atadevices_t *drive){
   struct ata_smart_values tempsmartval;
   struct ata_smart_thresholds tempsmartthres;
-  int failed;
+  int failed,fd;
   char *loc,attributename[64];
 
+  // 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)
+    return 1;
+  
   // Coming into this function, *drive contains the last values measured,
   // and we read the NEW values into tempsmartval
-  if (ataReadSmartValues(drive->fd,&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 (drive->fd, &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
+  // 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
@@ -291,18 +332,15 @@ int ataCheckDevice( atadevices_t *drive){
     printout(LOG_CRIT,"Device: %s, Failed SMART attribute: %s. Use smartctl -a %s.\n",
 	     drive->devicename,loc,drive->devicename);
   }
-
-  // WHEN IT WORKS, we should here add a call to ataSmartStatus2()
-  // either in addition to or instead of the ataCheckSmart command
-  // above. This is the "right" long-term solution.
   
   // see if any values have changed.  Second argument is new values
-  ataCompareSmartValues (drive , tempsmartval);
+  ataCompareSmartValues(drive, tempsmartval);
   
   // Save the new values into *drive for the next time around
   drive->smartval = tempsmartval;
   drive->smartthres = tempsmartthres;
   
+  closedevice(fd);
   return 0;
 }
 
@@ -312,10 +350,16 @@ int scsiCheckDevice( scsidevices_t *drive){
   UINT8 returnvalue;
   UINT8 currenttemp;
   UINT8 triptemp;
-  
+  int fd;
+
+  // 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)
+    return 1;
+
   currenttemp = triptemp = 0;
   
-  if (scsiCheckSmart( drive->fd, drive->SmartPageSupported, &returnvalue, &currenttemp, &triptemp ) != 0)
+  if (scsiCheckSmart(fd, drive->SmartPageSupported, &returnvalue, &currenttemp, &triptemp))
     printout(LOG_INFO, "%s:Failed to read SMART values\n", drive->devicename);
   
   if (returnvalue)
@@ -327,32 +371,34 @@ int scsiCheckDevice( scsidevices_t *drive){
   // Seems to completely ignore what capabilities were found on the
   // device when scanned
   if (currenttemp){
-    if ( (currenttemp != drive->Temperature) && ( drive->Temperature) )
+    if ((currenttemp != drive->Temperature) && (drive->Temperature))
       printout(LOG_INFO, "Device: %s, Temperature changed %d degrees to %d degrees since last reading\n", 
 	       drive->devicename, (int) (currenttemp - drive->Temperature), (unsigned int) currenttemp );
-    
     drive->Temperature = currenttemp;
-  } 
+  }
+  closedevice(fd);
   return 0;
 }
 
 void CheckDevices (  atadevices_t *atadevices, scsidevices_t *scsidevices){
   int i;
   
+  // If there are no devices to monitor, then exit
   if (!numatadevices && !numscsidevices){
     printout(LOG_INFO,"Unable to monitor any SMART enabled ATA or SCSI devices.\n");
     return;
   }
 
+  // Infinite loop, which checkes devices
   printout(LOG_INFO,"Started monitoring %d ATA and %d SCSI devices\n",numatadevices,numscsidevices);
   while (1){
-    for (i = 0; i < numatadevices;i++)
-      ataCheckDevice (  &atadevices[i]);
+    for (i=0; i<numatadevices; i++) 
+      ataCheckDevice(atadevices+i);
     
-    for (i = 0; i < numscsidevices;i++)
-      scsiCheckDevice (  &scsidevices[i]);
+    for (i=0; i<numscsidevices; i++)
+      scsiCheckDevice(scsidevices+i);
     
-    sleep ( checktime );
+    sleep(checktime);
   }
 }
 
@@ -363,7 +409,7 @@ int massagecvs(char *out,const char *in){
   const char *savein=in;
 
   // skip to I of $Id:
-  while (*in !='\0' && *in!='I')
+  while (*in && *in!='I')
     in++;
   
   // skip to start of filename
@@ -442,10 +488,10 @@ int parseconfigfile(){
   if (fp==NULL && errno!=ENOENT){
     // file exists but we can't read it
     if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Unable to open configuration file %s\n",
+      printout(LOG_CRIT,"%s: Unable to open configuration file %s\n",
 	       sys_errlist[errno],CONFIGFILE);
     else
-      printout(LOG_INFO,"Unable to open configuration file %s\n",CONFIGFILE);
+      printout(LOG_CRIT,"Unable to open configuration file %s\n",CONFIGFILE);
     exit(1);
   }
   
@@ -465,7 +511,7 @@ int parseconfigfile(){
     // See if line is too long
     len=strlen(line);
     if (len>MAXLINELEN){
-      printout(LOG_INFO,"Error: line %d of file %s is more than than %d characters long.\n",
+      printout(LOG_CRIT,"Error: line %d of file %s is more than than %d characters long.\n",
 	       lineno,CONFIGFILE,MAXLINELEN);
       exit(1); 
     }
@@ -488,7 +534,7 @@ int parseconfigfile(){
     
     // We've got a legit entry
     if (entry>=MAXENTRIES){
-      printout(LOG_INFO,"Error: configuration file %s can have no more than %d entries\n",
+      printout(LOG_CRIT,"Error: configuration file %s can have no more than %d entries\n",
 	       CONFIGFILE,MAXENTRIES);
       exit(1);
     }
@@ -511,25 +557,21 @@ int parseconfigfile(){
   if (entry)
     return entry;
   
-  printout(LOG_INFO,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
+  printout(LOG_CRIT,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
   exit(1);
 }
 
-const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'h','?','\0' };
+// const char opts[] = {DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'h','?','\0' };
+const char opts[] = {DEBUGMODE, PRINTCOPYLEFT,'h','?','\0' };
 
-/* Main Program */
-int main (int argc, char **argv){
-  atadevices_t atadevices[MAXATADEVICES], *atadevicesptr;
-  scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr;
-  int optchar,i;
+
+// Parses input line, prints usage message and
+// version/license/copyright messages
+void ParseOpts(int argc, char **argv){
   extern char *optarg;
   extern int  optopt, optind, opterr;
-  int entries;
-  
-  numatadevices=0;
-  numscsidevices=0;
-  scsidevicesptr = scsidevices;
-  atadevicesptr = atadevices;
+  int optchar;
+
   opterr=optopt=0;
 
   // Parse input options:
@@ -550,7 +592,7 @@ int main (int argc, char **argv){
       debugmode=1;
       if (optopt) {
 	printhead();
-	printout(LOG_INFO,"=======> UNRECOGNIZED OPTION: %c <======= \n\n",optopt);
+	printout(LOG_CRIT,"=======> UNRECOGNIZED OPTION: %c <======= \n\n",optopt);
 	Usage();
 	exit(-1);
       }
@@ -574,6 +616,19 @@ int main (int argc, char **argv){
   
   // print header
   printhead();
+  return;
+}
+
+/* Main Program */
+int main (int argc, char **argv){
+  atadevices_t atadevices[MAXATADEVICES], *atadevicesptr=atadevices;
+  scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr=scsidevices;
+  int i,entries;
+  
+  numatadevices=numscsidevices=0;
+  
+  // Parse input and print header and usage info if needed
+  ParseOpts(argc,argv);
  
   // look in configuration file CONFIGFILE (normally /etc/smartd.conf)
   entries=parseconfigfile();
@@ -583,18 +638,20 @@ int main (int argc, char **argv){
     daemon_init();
   }
   
-  // If we found a config file, look at its entries
+  // 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);
@@ -604,6 +661,7 @@ int main (int argc, char **argv){
       scsidevicescan(scsidevicesptr, devicescsi);
   }
   
+  // Now start an infinite loop that checks all devices
   CheckDevices(atadevicesptr, scsidevicesptr); 
   return 0;
 }
diff --git a/sm5/smartd.cpp b/sm5/smartd.cpp
index 63546130d..f376bbfdf 100644
--- a/sm5/smartd.cpp
+++ b/sm5/smartd.cpp
@@ -37,41 +37,11 @@
 #include "scsicmds.h"
 #include "smartd.h"
 
+// CVS ID strings
 extern const char *CVSid1, *CVSid2;
-const char *CVSid3="$Id: smartd.cpp,v 1.31 2002/10/25 14:54:14 ballen4705 Exp $" 
+const char *CVSid3="$Id: smartd.cpp,v 1.32 2002/10/26 09:24:26 ballen4705 Exp $" 
 CVSID1 CVSID4 CVSID7;
 
-int daemon_init(void){
-  pid_t pid;
-  int i;  
-
-  if ( (pid = fork()) < 0)
-    // unable to fork!
-    exit(1);
-  else if (pid != 0)
-    // we are the parent process -- exit cleanly
-    exit (0);
-
-  // from here on, we are the child process
-  setsid();
-
-  // close any open file descriptors
-  for (i=getdtablesize();i>=0;--i)
-    close(i);
-
-  // redirect any IO attempts to /dev/null
-  // open stdin
-  i=open("/dev/null",O_RDWR);
-  // stdout
-  dup(i);
-  // stderr
-  dup(i);
-  umask(0);
-  chdir("/");
-  return(0);
-}
-
-
 // This function prints either to stdout or to the syslog as needed
 void printout(int priority,char *fmt, ...){
   va_list ap;
@@ -85,22 +55,54 @@ void printout(int priority,char *fmt, ...){
   return;
 }
 
-// Printing function for atacmds
+// Printing function for debugging atacmds. For debugging set 0 to 1
+// in #if statement
 void pout(char *fmt, ...){
   va_list ap;
+  
   // initialize variable argument list 
   va_start(ap,fmt);
-  va_end(ap);
-  return;
 #if (0)
-  // print out
   vprintf(fmt,ap);
+#endif
   va_end(ap);
   return;
-#endif
 }
 
+// Forks new process, closes all file descriptors, redirects stdin,
+// stdout, stderr
+int daemon_init(void){
+  pid_t pid;
+  int i;  
 
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    printout(LOG_CRIT,"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
+  setsid();
+
+  // close any open file descriptors
+  for (i=getdtablesize();i>=0;--i)
+    close(i);
+  
+  // redirect any IO attempts to /dev/null for stdin
+  i=open("/dev/null",O_RDWR);
+  // stdout
+  dup(i);
+  // stderr
+  dup(i);
+  umask(0);
+  chdir("/");
+  return(0);
+}
+
+// Prints header identifying version of code and home
 void printhead(){
   printout(LOG_INFO,"smartd version %d.%d-%d - S.M.A.R.T. Daemon.\n",
            RELEASE_MAJOR, RELEASE_MINOR, SMARTMONTOOLS_VERSION);
@@ -114,23 +116,45 @@ void Usage (void){
   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,"Configuration file: /etc/smartd.conf\n");
+  printout(LOG_INFO,"Optional configuration file: %s\n",CONFIGFILE);
 }
-	
+
+// returns negative if problem, else fd>=0
+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);
+    else
+      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+    return -1;
+  }
+  // device opened sucessfully
+  return fd;
+}
+
+// returns 1 if problem, else zero
+int closedevice(int fd){
+  if (close(fd)){
+    if (errno<sys_nerr)
+      printout(LOG_INFO,"%s: Closing file descriptor %d failed\n",sys_errlist[errno],fd);
+    else
+      printout(LOG_INFO,"Closing file descriptor %d failed\n",fd);
+    return 1;
+  }
+  // device opened sucessfully
+  return 0;
+}
+
 // scan to see what ata devices there are, and if they support SMART
 int atadevicescan (atadevices_t *devices, char *device){
   int fd;
   struct hd_driveid drive;
   
   printout(LOG_INFO,"Opening device %s\n", device);
-  fd = open(device, O_RDONLY);
-  if (fd < 0) {
-    if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
-    else
-      printout(LOG_INFO,"Device: %s, Opening device failed\n",device);
+  if ((fd=opendevice(device))<0)
+    // device open failed
     return 1;
-  }
   
   if (ataReadHDIdentity (fd,&drive) || !ataSmartSupport(drive) || ataEnableSmart(fd)){
     // device exists, but not able to do SMART
@@ -153,7 +177,13 @@ int atadevicescan (atadevices_t *devices, char *device){
     return 4;
   }
   
-  // device exists, and does SMART.  Add to list
+  // Device exists, and does SMART.  Add to list
+  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);
   devices[numatadevices].fd = fd;
   strcpy(devices[numatadevices].devicename, device);
@@ -167,6 +197,7 @@ int atadevicescan (atadevices_t *devices, char *device){
     isSupportSelfTest(devices[numatadevices].smartval);
   
   numatadevices++;
+  closedevice(fd);
   return 0;
 }
 
@@ -177,18 +208,11 @@ int scsidevicescan (scsidevices_t *devices, char *device){
   int i, fd, smartsupport;
   unsigned char  tBuf[4096];
   
-  
-  // open device
   printout(LOG_INFO,"Opening device %s\n", device);
-  fd=open(device, O_RDWR);
-  if (fd<0) {
-    if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Device: %s, Opening device failed\n",sys_errlist[errno],device);
-    else
-      printout(LOG_INFO,"Device: %s, Opening device failed\n", device);
+  if ((fd=opendevice(device))<0)
+    // device open failed
     return 1;
-  }
-
+  
   // check that it's ready for commands
   if (!testunitready(fd)){
     printout(LOG_INFO,"Device: %s, Failed Test Unit Ready\n", device);
@@ -211,6 +235,13 @@ int scsidevicescan (scsidevices_t *devices, char *device){
     return 4;
   }
 
+  // Device exists, and does SMART.  Add to list
+  if (numscsidevices>=MAXSCSIDEVICES){
+    printout(LOG_CRIT,"smartd has found more than MAXSCSIDEVICES=%d SCSI devices.\n"
+	     "Recompile code from " PROJECTHOME " with larger MAXSCSIDEVICES\n",numscsidevices);
+    exit(1);
+  }
+
   // now we can proceed to register the device
   printout(LOG_INFO, "Device: %s, Found and is SMART capable. Adding to \"monitor\" list.\n",device);
   devices[numscsidevices].fd = fd;
@@ -233,6 +264,7 @@ int scsidevicescan (scsidevices_t *devices, char *device){
     }	
   }
   numscsidevices++;
+  closedevice(fd);
   return 0;
 }
 
@@ -269,19 +301,28 @@ void ataCompareSmartValues (atadevices_t *device, struct ata_smart_values new ){
 int ataCheckDevice( atadevices_t *drive){
   struct ata_smart_values tempsmartval;
   struct ata_smart_thresholds tempsmartthres;
-  int failed;
+  int failed,fd;
   char *loc,attributename[64];
 
+  // 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)
+    return 1;
+  
   // Coming into this function, *drive contains the last values measured,
   // and we read the NEW values into tempsmartval
-  if (ataReadSmartValues(drive->fd,&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 (drive->fd, &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
+  // 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
@@ -291,18 +332,15 @@ int ataCheckDevice( atadevices_t *drive){
     printout(LOG_CRIT,"Device: %s, Failed SMART attribute: %s. Use smartctl -a %s.\n",
 	     drive->devicename,loc,drive->devicename);
   }
-
-  // WHEN IT WORKS, we should here add a call to ataSmartStatus2()
-  // either in addition to or instead of the ataCheckSmart command
-  // above. This is the "right" long-term solution.
   
   // see if any values have changed.  Second argument is new values
-  ataCompareSmartValues (drive , tempsmartval);
+  ataCompareSmartValues(drive, tempsmartval);
   
   // Save the new values into *drive for the next time around
   drive->smartval = tempsmartval;
   drive->smartthres = tempsmartthres;
   
+  closedevice(fd);
   return 0;
 }
 
@@ -312,10 +350,16 @@ int scsiCheckDevice( scsidevices_t *drive){
   UINT8 returnvalue;
   UINT8 currenttemp;
   UINT8 triptemp;
-  
+  int fd;
+
+  // 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)
+    return 1;
+
   currenttemp = triptemp = 0;
   
-  if (scsiCheckSmart( drive->fd, drive->SmartPageSupported, &returnvalue, &currenttemp, &triptemp ) != 0)
+  if (scsiCheckSmart(fd, drive->SmartPageSupported, &returnvalue, &currenttemp, &triptemp))
     printout(LOG_INFO, "%s:Failed to read SMART values\n", drive->devicename);
   
   if (returnvalue)
@@ -327,32 +371,34 @@ int scsiCheckDevice( scsidevices_t *drive){
   // Seems to completely ignore what capabilities were found on the
   // device when scanned
   if (currenttemp){
-    if ( (currenttemp != drive->Temperature) && ( drive->Temperature) )
+    if ((currenttemp != drive->Temperature) && (drive->Temperature))
       printout(LOG_INFO, "Device: %s, Temperature changed %d degrees to %d degrees since last reading\n", 
 	       drive->devicename, (int) (currenttemp - drive->Temperature), (unsigned int) currenttemp );
-    
     drive->Temperature = currenttemp;
-  } 
+  }
+  closedevice(fd);
   return 0;
 }
 
 void CheckDevices (  atadevices_t *atadevices, scsidevices_t *scsidevices){
   int i;
   
+  // If there are no devices to monitor, then exit
   if (!numatadevices && !numscsidevices){
     printout(LOG_INFO,"Unable to monitor any SMART enabled ATA or SCSI devices.\n");
     return;
   }
 
+  // Infinite loop, which checkes devices
   printout(LOG_INFO,"Started monitoring %d ATA and %d SCSI devices\n",numatadevices,numscsidevices);
   while (1){
-    for (i = 0; i < numatadevices;i++)
-      ataCheckDevice (  &atadevices[i]);
+    for (i=0; i<numatadevices; i++) 
+      ataCheckDevice(atadevices+i);
     
-    for (i = 0; i < numscsidevices;i++)
-      scsiCheckDevice (  &scsidevices[i]);
+    for (i=0; i<numscsidevices; i++)
+      scsiCheckDevice(scsidevices+i);
     
-    sleep ( checktime );
+    sleep(checktime);
   }
 }
 
@@ -363,7 +409,7 @@ int massagecvs(char *out,const char *in){
   const char *savein=in;
 
   // skip to I of $Id:
-  while (*in !='\0' && *in!='I')
+  while (*in && *in!='I')
     in++;
   
   // skip to start of filename
@@ -442,10 +488,10 @@ int parseconfigfile(){
   if (fp==NULL && errno!=ENOENT){
     // file exists but we can't read it
     if (errno<sys_nerr)
-      printout(LOG_INFO,"%s: Unable to open configuration file %s\n",
+      printout(LOG_CRIT,"%s: Unable to open configuration file %s\n",
 	       sys_errlist[errno],CONFIGFILE);
     else
-      printout(LOG_INFO,"Unable to open configuration file %s\n",CONFIGFILE);
+      printout(LOG_CRIT,"Unable to open configuration file %s\n",CONFIGFILE);
     exit(1);
   }
   
@@ -465,7 +511,7 @@ int parseconfigfile(){
     // See if line is too long
     len=strlen(line);
     if (len>MAXLINELEN){
-      printout(LOG_INFO,"Error: line %d of file %s is more than than %d characters long.\n",
+      printout(LOG_CRIT,"Error: line %d of file %s is more than than %d characters long.\n",
 	       lineno,CONFIGFILE,MAXLINELEN);
       exit(1); 
     }
@@ -488,7 +534,7 @@ int parseconfigfile(){
     
     // We've got a legit entry
     if (entry>=MAXENTRIES){
-      printout(LOG_INFO,"Error: configuration file %s can have no more than %d entries\n",
+      printout(LOG_CRIT,"Error: configuration file %s can have no more than %d entries\n",
 	       CONFIGFILE,MAXENTRIES);
       exit(1);
     }
@@ -511,25 +557,21 @@ int parseconfigfile(){
   if (entry)
     return entry;
   
-  printout(LOG_INFO,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
+  printout(LOG_CRIT,"Configuration file %s contained no devices (like /dev/hda)\n",CONFIGFILE);
   exit(1);
 }
 
-const char opts[] = { DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'h','?','\0' };
+// const char opts[] = {DEBUGMODE, EMAILNOTIFICATION, PRINTCOPYLEFT,'h','?','\0' };
+const char opts[] = {DEBUGMODE, PRINTCOPYLEFT,'h','?','\0' };
 
-/* Main Program */
-int main (int argc, char **argv){
-  atadevices_t atadevices[MAXATADEVICES], *atadevicesptr;
-  scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr;
-  int optchar,i;
+
+// Parses input line, prints usage message and
+// version/license/copyright messages
+void ParseOpts(int argc, char **argv){
   extern char *optarg;
   extern int  optopt, optind, opterr;
-  int entries;
-  
-  numatadevices=0;
-  numscsidevices=0;
-  scsidevicesptr = scsidevices;
-  atadevicesptr = atadevices;
+  int optchar;
+
   opterr=optopt=0;
 
   // Parse input options:
@@ -550,7 +592,7 @@ int main (int argc, char **argv){
       debugmode=1;
       if (optopt) {
 	printhead();
-	printout(LOG_INFO,"=======> UNRECOGNIZED OPTION: %c <======= \n\n",optopt);
+	printout(LOG_CRIT,"=======> UNRECOGNIZED OPTION: %c <======= \n\n",optopt);
 	Usage();
 	exit(-1);
       }
@@ -574,6 +616,19 @@ int main (int argc, char **argv){
   
   // print header
   printhead();
+  return;
+}
+
+/* Main Program */
+int main (int argc, char **argv){
+  atadevices_t atadevices[MAXATADEVICES], *atadevicesptr=atadevices;
+  scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr=scsidevices;
+  int i,entries;
+  
+  numatadevices=numscsidevices=0;
+  
+  // Parse input and print header and usage info if needed
+  ParseOpts(argc,argv);
  
   // look in configuration file CONFIGFILE (normally /etc/smartd.conf)
   entries=parseconfigfile();
@@ -583,18 +638,20 @@ int main (int argc, char **argv){
     daemon_init();
   }
   
-  // If we found a config file, look at its entries
+  // 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);
@@ -604,6 +661,7 @@ int main (int argc, char **argv){
       scsidevicescan(scsidevicesptr, devicescsi);
   }
   
+  // 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 43d9a1b0e..74fdb9765 100644
--- a/sm5/smartd.h
+++ b/sm5/smartd.h
@@ -23,14 +23,24 @@
  */
 
 #ifndef CVSID7
-#define CVSID7 "$Id: smartd.h,v 1.8 2002/10/25 14:15:05 ballen4705 Exp $\n"
+#define CVSID7 "$Id: smartd.h,v 1.9 2002/10/26 09:24:26 ballen4705 Exp $\n"
 #endif
 
 // Configuration file
 #define CONFIGFILE "/etc/smartd.conf"
-#define MAXLINELEN 126
+#define MAXLINELEN 114
 #define MAXENTRIES 64
 
+// BAD PROGRAMMING PRACTICE - GLOBAL VARIABLES SHOULD BE IN .c NOT .h
+// FILE
+/* how often SMART status is checked, in seconds */
+int checktime = 1800;
+// number of ATA and SCSI devices being watched
+int numatadevices;
+int numscsidevices;
+#define MAXATADEVICES	12
+#define MAXSCSIDEVICES	26
+
 /* Defines for command line options */ 
 #define DEBUGMODE 		'X'
 #define EMAILNOTIFICATION	'e'
@@ -40,42 +50,32 @@
 #define TRUE 0x01
 #define FALSE 0x00
 
-#define MAXATADEVICES	12
-#define MAXSCSIDEVICES	26
-
 /* Global Variables for command line options */
+// These should go into a structure at some point
 unsigned char debugmode               = FALSE;
 unsigned char emailnotification       = FALSE;
 unsigned char printcopyleft           = FALSE;
 
-/* Number of ata device to scan */
-int numatadevices;
-int numscsidevices;
-
-
-/* how often SMART is checks in seconds */
-int checktime = 1800;
-
 typedef struct atadevices_s {
 	int fd;
-	char devicename[14];
 	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 {
 	int fd;
-	char devicename[14];
 	unsigned char SmartPageSupported;
 	unsigned char TempPageSupported;
 	unsigned char Temperature;
+	char devicename[MAXLINELEN+2];
 } scsidevices_t;
 
 typedef struct configfile_s {
-  char name[MAXLINELEN+2];
+  int lineno;
   int tryata;
   int tryscsi;
-  int lineno;
+  char name[MAXLINELEN+2];  // really only needs to be +1
 } cfgfile;
-- 
GitLab