smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
smartd.cpp
Go to the documentation of this file.
00001 /*
00002  * Home page of code is: http://smartmontools.sourceforge.net
00003  *
00004  * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
00005  * Copyright (C) 2000    Michael Cornwell <cornwell@acm.org>
00006  * Copyright (C) 2008    Oliver Bock <brevilo@users.sourceforge.net>
00007  * Copyright (C) 2008-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2, or (at your option)
00012  * any later version.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * This code was originally developed as a Senior Thesis by Michael Cornwell
00018  * at the Concurrent Systems Laboratory (now part of the Storage Systems
00019  * Research Center), Jack Baskin School of Engineering, University of
00020  * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
00021  *
00022  */
00023 
00024 #include "config.h"
00025 #include "int64.h"
00026 
00027 // unconditionally included files
00028 #include <stdio.h>
00029 #include <sys/types.h>
00030 #include <sys/stat.h>   // umask
00031 #include <signal.h>
00032 #include <fcntl.h>
00033 #include <string.h>
00034 #include <syslog.h>
00035 #include <stdarg.h>
00036 #include <stdlib.h>
00037 #include <errno.h>
00038 #include <time.h>
00039 #include <limits.h>
00040 #include <getopt.h>
00041 
00042 #include <stdexcept>
00043 #include <string>
00044 #include <vector>
00045 #include <algorithm> // std::replace()
00046 
00047 // conditionally included files
00048 #ifndef _WIN32
00049 #include <sys/wait.h>
00050 #endif
00051 #ifdef HAVE_UNISTD_H
00052 #include <unistd.h>
00053 #endif
00054 #ifdef HAVE_NETDB_H
00055 #include <netdb.h>
00056 #endif
00057 
00058 #ifdef _WIN32
00059 #ifdef _MSC_VER
00060 #pragma warning(disable:4761) // "conversion supplied"
00061 typedef unsigned short mode_t;
00062 typedef int pid_t;
00063 #endif
00064 #include <io.h> // umask()
00065 #include <process.h> // getpid()
00066 #endif // _WIN32
00067 
00068 #ifdef __CYGWIN__
00069 #include <io.h> // setmode()
00070 #endif // __CYGWIN__
00071 
00072 #ifdef HAVE_LIBCAP_NG
00073 #include <cap-ng.h>
00074 #endif // LIBCAP_NG
00075 
00076 // locally included files
00077 #include "atacmds.h"
00078 #include "dev_interface.h"
00079 #include "knowndrives.h"
00080 #include "scsicmds.h"
00081 #include "utility.h"
00082 
00083 // This is for solaris, where signal() resets the handler to SIG_DFL
00084 // after the first signal is caught.
00085 #ifdef HAVE_SIGSET
00086 #define SIGNALFN sigset
00087 #else
00088 #define SIGNALFN signal
00089 #endif
00090 
00091 #ifdef _WIN32
00092 // fork()/signal()/initd simulation for native Windows
00093 #include "daemon_win32.h" // daemon_main/detach/signal()
00094 #undef SIGNALFN
00095 #define SIGNALFN  daemon_signal
00096 #define strsignal daemon_strsignal
00097 #define sleep     daemon_sleep
00098 // SIGQUIT does not exist, CONTROL-Break signals SIGBREAK.
00099 #define SIGQUIT SIGBREAK
00100 #define SIGQUIT_KEYNAME "CONTROL-Break"
00101 #else  // _WIN32
00102 #define SIGQUIT_KEYNAME "CONTROL-\\"
00103 #endif // _WIN32
00104 
00105 #if defined (__SVR4) && defined (__sun)
00106 extern "C" int getdomainname(char *, int); // no declaration in header files!
00107 #endif
00108 
00109 const char * smartd_cpp_cvsid = "$Id: smartd.cpp 3851 2013-08-17 20:10:11Z chrfranke $"
00110   CONFIG_H_CVSID;
00111 
00112 // smartd exit codes
00113 #define EXIT_BADCMD    1   // command line did not parse
00114 #define EXIT_BADCONF   2   // syntax error in config file
00115 #define EXIT_STARTUP   3   // problem forking daemon
00116 #define EXIT_PID       4   // problem creating pid file
00117 #define EXIT_NOCONF    5   // config file does not exist
00118 #define EXIT_READCONF  6   // config file exists but cannot be read
00119 
00120 #define EXIT_NOMEM     8   // out of memory
00121 #define EXIT_BADCODE   10  // internal error - should NEVER happen
00122 
00123 #define EXIT_BADDEV    16  // we can't monitor this device
00124 #define EXIT_NODEV     17  // no devices to monitor
00125 
00126 #define EXIT_SIGNAL    254 // abort on signal
00127 
00128 
00129 // command-line: 1=debug mode, 2=print presets
00130 static unsigned char debugmode = 0;
00131 
00132 // command-line: how long to sleep between checks
00133 #define CHECKTIME 1800
00134 static int checktime=CHECKTIME;
00135 
00136 // command-line: name of PID file (empty for no pid file)
00137 static std::string pid_file;
00138 
00139 // command-line: path prefix of persistent state file, empty if no persistence.
00140 static std::string state_path_prefix
00141 #ifdef SMARTMONTOOLS_SAVESTATES
00142           = SMARTMONTOOLS_SAVESTATES
00143 #endif
00144                                     ;
00145 
00146 // command-line: path prefix of attribute log file, empty if no logs.
00147 static std::string attrlog_path_prefix
00148 #ifdef SMARTMONTOOLS_ATTRIBUTELOG
00149           = SMARTMONTOOLS_ATTRIBUTELOG
00150 #endif
00151                                     ;
00152 
00153 // configuration file name
00154 static const char * configfile;
00155 // configuration file "name" if read from stdin
00156 static const char * const configfile_stdin = "<stdin>";
00157 // path of alternate configuration file
00158 static std::string configfile_alt;
00159 
00160 // warning script file
00161 static std::string warning_script;
00162 
00163 // command-line: when should we exit?
00164 static int quit=0;
00165 
00166 // command-line; this is the default syslog(3) log facility to use.
00167 static int facility=LOG_DAEMON;
00168 
00169 #ifndef _WIN32
00170 // command-line: fork into background?
00171 static bool do_fork=true;
00172 #endif
00173 
00174 #ifdef HAVE_LIBCAP_NG
00175 // command-line: enable capabilities?
00176 static bool enable_capabilities = false;
00177 #endif
00178 
00179 // TODO: This smartctl only variable is also used in os_win32.cpp
00180 unsigned char failuretest_permissive = 0;
00181 
00182 // set to one if we catch a USR1 (check devices now)
00183 static volatile int caughtsigUSR1=0;
00184 
00185 #ifdef _WIN32
00186 // set to one if we catch a USR2 (toggle debug mode)
00187 static volatile int caughtsigUSR2=0;
00188 #endif
00189 
00190 // set to one if we catch a HUP (reload config file). In debug mode,
00191 // set to two, if we catch INT (also reload config file).
00192 static volatile int caughtsigHUP=0;
00193 
00194 // set to signal value if we catch INT, QUIT, or TERM
00195 static volatile int caughtsigEXIT=0;
00196 
00197 // This function prints either to stdout or to the syslog as needed.
00198 static void PrintOut(int priority, const char *fmt, ...)
00199                      __attribute_format_printf(2, 3);
00200 
00201 // Attribute monitoring flags.
00202 // See monitor_attr_flags below.
00203 enum {
00204   MONITOR_IGN_FAILUSE = 0x01,
00205   MONITOR_IGNORE      = 0x02,
00206   MONITOR_RAW_PRINT   = 0x04,
00207   MONITOR_RAW         = 0x08,
00208   MONITOR_AS_CRIT     = 0x10,
00209   MONITOR_RAW_AS_CRIT = 0x20,
00210 };
00211 
00212 // Array of flags for each attribute.
00213 class attribute_flags
00214 {
00215 public:
00216   attribute_flags()
00217     { memset(m_flags, 0, sizeof(m_flags)); }
00218 
00219   bool is_set(int id, unsigned char flag) const
00220     { return (0 < id && id < (int)sizeof(m_flags) && (m_flags[id] & flag)); }
00221 
00222   void set(int id, unsigned char flags)
00223     {
00224       if (0 < id && id < (int)sizeof(m_flags))
00225         m_flags[id] |= flags;
00226     }
00227 
00228 private:
00229   unsigned char m_flags[256];
00230 };
00231 
00232 
00233 /// Configuration data for a device. Read from smartd.conf.
00234 /// Supports copy & assignment and is compatible with STL containers.
00235 struct dev_config
00236 {
00237   int lineno;                             // Line number of entry in file
00238   std::string name;                       // Device name (with optional extra info)
00239   std::string dev_name;                   // Device name (plain, for SMARTD_DEVICE variable)
00240   std::string dev_type;                   // Device type argument from -d directive, empty if none
00241   std::string dev_idinfo;                 // Device identify info for warning emails
00242   std::string state_file;                 // Path of the persistent state file, empty if none
00243   std::string attrlog_file;               // Path of the persistent attrlog file, empty if none
00244   bool ignore;                            // Ignore this entry
00245   bool smartcheck;                        // Check SMART status
00246   bool usagefailed;                       // Check for failed Usage Attributes
00247   bool prefail;                           // Track changes in Prefail Attributes
00248   bool usage;                             // Track changes in Usage Attributes
00249   bool selftest;                          // Monitor number of selftest errors
00250   bool errorlog;                          // Monitor number of ATA errors
00251   bool xerrorlog;                         // Monitor number of ATA errors (Extended Comprehensive error log)
00252   bool offlinests;                        // Monitor changes in offline data collection status
00253   bool offlinests_ns;                     // Disable auto standby if in progress
00254   bool selfteststs;                       // Monitor changes in self-test execution status
00255   bool selfteststs_ns;                    // Disable auto standby if in progress
00256   bool permissive;                        // Ignore failed SMART commands
00257   char autosave;                          // 1=disable, 2=enable Autosave Attributes
00258   char autoofflinetest;                   // 1=disable, 2=enable Auto Offline Test
00259   firmwarebug_defs firmwarebugs;          // -F directives from drivedb or smartd.conf
00260   bool ignorepresets;                     // Ignore database of -v options
00261   bool showpresets;                       // Show database entry for this device
00262   bool removable;                         // Device may disappear (not be present)
00263   char powermode;                         // skip check, if disk in idle or standby mode
00264   bool powerquiet;                        // skip powermode 'skipping checks' message
00265   int powerskipmax;                       // how many times can be check skipped
00266   unsigned char tempdiff;                 // Track Temperature changes >= this limit
00267   unsigned char tempinfo, tempcrit;       // Track Temperatures >= these limits as LOG_INFO, LOG_CRIT+mail
00268   regular_expression test_regex;          // Regex for scheduled testing
00269 
00270   // Configuration of email warning messages
00271   std::string emailcmdline;               // script to execute, empty if no messages
00272   std::string emailaddress;               // email address, or empty
00273   unsigned char emailfreq;                // Emails once (1) daily (2) diminishing (3)
00274   bool emailtest;                         // Send test email?
00275 
00276   // ATA ONLY
00277   int dev_rpm; // rotation rate, 0 = unknown, 1 = SSD, >1 = HDD
00278   int set_aam; // disable(-1), enable(1..255->0..254) Automatic Acoustic Management
00279   int set_apm; // disable(-1), enable(2..255->1..254) Advanced Power Management
00280   int set_lookahead; // disable(-1), enable(1) read look-ahead
00281   int set_standby; // set(1..255->0..254) standby timer
00282   bool set_security_freeze; // Freeze ATA security
00283   int set_wcache; // disable(-1), enable(1) write cache
00284 
00285   bool sct_erc_set;                       // set SCT ERC to:
00286   unsigned short sct_erc_readtime;        // ERC read time (deciseconds)
00287   unsigned short sct_erc_writetime;       // ERC write time (deciseconds)
00288 
00289   unsigned char curr_pending_id;          // ID of current pending sector count, 0 if none
00290   unsigned char offl_pending_id;          // ID of offline uncorrectable sector count, 0 if none
00291   bool curr_pending_incr, offl_pending_incr; // True if current/offline pending values increase
00292   bool curr_pending_set,  offl_pending_set;  // True if '-C', '-U' set in smartd.conf
00293 
00294   attribute_flags monitor_attr_flags;     // MONITOR_* flags for each attribute
00295 
00296   ata_vendor_attr_defs attribute_defs;    // -v options
00297 
00298   dev_config();
00299 };
00300 
00301 dev_config::dev_config()
00302 : lineno(0),
00303   ignore(false),
00304   smartcheck(false),
00305   usagefailed(false),
00306   prefail(false),
00307   usage(false),
00308   selftest(false),
00309   errorlog(false),
00310   xerrorlog(false),
00311   offlinests(false),  offlinests_ns(false),
00312   selfteststs(false), selfteststs_ns(false),
00313   permissive(false),
00314   autosave(0),
00315   autoofflinetest(0),
00316   ignorepresets(false),
00317   showpresets(false),
00318   removable(false),
00319   powermode(0),
00320   powerquiet(false),
00321   powerskipmax(0),
00322   tempdiff(0),
00323   tempinfo(0), tempcrit(0),
00324   emailfreq(0),
00325   emailtest(false),
00326   dev_rpm(0),
00327   set_aam(0), set_apm(0),
00328   set_lookahead(0),
00329   set_standby(0),
00330   set_security_freeze(false),
00331   set_wcache(0),
00332   sct_erc_set(false),
00333   sct_erc_readtime(0), sct_erc_writetime(0),
00334   curr_pending_id(0), offl_pending_id(0),
00335   curr_pending_incr(false), offl_pending_incr(false),
00336   curr_pending_set(false),  offl_pending_set(false)
00337 {
00338 }
00339 
00340 
00341 // Number of allowed mail message types
00342 static const int SMARTD_NMAIL = 13;
00343 // Type for '-M test' mails (state not persistent)
00344 static const int MAILTYPE_TEST = 0;
00345 // TODO: Add const or enum for all mail types.
00346 
00347 struct mailinfo {
00348   int logged;// number of times an email has been sent
00349   time_t firstsent;// time first email was sent, as defined by time(2)
00350   time_t lastsent; // time last email was sent, as defined by time(2)
00351 
00352   mailinfo()
00353     : logged(0), firstsent(0), lastsent(0) { }
00354 };
00355 
00356 /// Persistent state data for a device.
00357 struct persistent_dev_state
00358 {
00359   unsigned char tempmin, tempmax;         // Min/Max Temperatures
00360 
00361   unsigned char selflogcount;             // total number of self-test errors
00362   unsigned short selfloghour;             // lifetime hours of last self-test error
00363 
00364   time_t scheduled_test_next_check;       // Time of next check for scheduled self-tests
00365 
00366   uint64_t selective_test_last_start;     // Start LBA of last scheduled selective self-test
00367   uint64_t selective_test_last_end;       // End LBA of last scheduled selective self-test
00368 
00369   mailinfo maillog[SMARTD_NMAIL];         // log info on when mail sent
00370 
00371   // ATA ONLY
00372   int ataerrorcount;                      // Total number of ATA errors
00373 
00374   // Persistent part of ata_smart_values:
00375   struct ata_attribute {
00376     unsigned char id;
00377     unsigned char val;
00378     unsigned char worst; // Byte needed for 'raw64' attribute only.
00379     uint64_t raw;
00380     unsigned char resvd;
00381 
00382     ata_attribute() : id(0), val(0), worst(0), raw(0), resvd(0) { }
00383   };
00384   ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES];
00385   
00386   // SCSI ONLY
00387 
00388   struct scsi_error_counter {
00389     struct scsiErrorCounter errCounter;
00390     unsigned char found;
00391     scsi_error_counter() : found(0) { }
00392   };
00393   scsi_error_counter scsi_error_counters[3];
00394 
00395   struct scsi_nonmedium_error {
00396     struct scsiNonMediumError nme;
00397     unsigned char found;
00398     scsi_nonmedium_error() : found(0) { }
00399   };
00400   scsi_nonmedium_error scsi_nonmedium_error;
00401 
00402   persistent_dev_state();
00403 };
00404 
00405 persistent_dev_state::persistent_dev_state()
00406 : tempmin(0), tempmax(0),
00407   selflogcount(0),
00408   selfloghour(0),
00409   scheduled_test_next_check(0),
00410   selective_test_last_start(0),
00411   selective_test_last_end(0),
00412   ataerrorcount(0)
00413 {
00414 }
00415 
00416 /// Non-persistent state data for a device.
00417 struct temp_dev_state
00418 {
00419   bool must_write;                        // true if persistent part should be written
00420 
00421   bool not_cap_offline;                   // true == not capable of offline testing
00422   bool not_cap_conveyance;
00423   bool not_cap_short;
00424   bool not_cap_long;
00425   bool not_cap_selective;
00426 
00427   unsigned char temperature;              // last recorded Temperature (in Celsius)
00428   time_t tempmin_delay;                   // time where Min Temperature tracking will start
00429 
00430   bool powermodefail;                     // true if power mode check failed
00431   int powerskipcnt;                       // Number of checks skipped due to idle or standby mode
00432 
00433   // SCSI ONLY
00434   unsigned char SmartPageSupported;       // has log sense IE page (0x2f)
00435   unsigned char TempPageSupported;        // has log sense temperature page (0xd)
00436   unsigned char ReadECounterPageSupported;
00437   unsigned char WriteECounterPageSupported;
00438   unsigned char VerifyECounterPageSupported;
00439   unsigned char NonMediumErrorPageSupported;
00440   unsigned char SuppressReport;           // minimize nuisance reports
00441   unsigned char modese_len;               // mode sense/select cmd len: 0 (don't
00442                                           // know yet) 6 or 10
00443   // ATA ONLY
00444   uint64_t num_sectors;                   // Number of sectors
00445   ata_smart_values smartval;              // SMART data
00446   ata_smart_thresholds_pvt smartthres;    // SMART thresholds
00447   bool offline_started;                   // true if offline data collection was started
00448   bool selftest_started;                  // true if self-test was started
00449 
00450   temp_dev_state();
00451 };
00452 
00453 temp_dev_state::temp_dev_state()
00454 : must_write(false),
00455   not_cap_offline(false),
00456   not_cap_conveyance(false),
00457   not_cap_short(false),
00458   not_cap_long(false),
00459   not_cap_selective(false),
00460   temperature(0),
00461   tempmin_delay(0),
00462   powermodefail(false),
00463   powerskipcnt(0),
00464   SmartPageSupported(false),
00465   TempPageSupported(false),
00466   ReadECounterPageSupported(false),
00467   WriteECounterPageSupported(false),
00468   VerifyECounterPageSupported(false),
00469   NonMediumErrorPageSupported(false),
00470   SuppressReport(false),
00471   modese_len(0),
00472   num_sectors(0),
00473   offline_started(false),
00474   selftest_started(false)
00475 {
00476   memset(&smartval, 0, sizeof(smartval));
00477   memset(&smartthres, 0, sizeof(smartthres));
00478 }
00479 
00480 /// Runtime state data for a device.
00481 struct dev_state
00482 : public persistent_dev_state,
00483   public temp_dev_state
00484 {
00485   void update_persistent_state();
00486   void update_temp_state();
00487 };
00488 
00489 /// Container for configuration info for each device.
00490 typedef std::vector<dev_config> dev_config_vector;
00491 
00492 /// Container for state info for each device.
00493 typedef std::vector<dev_state> dev_state_vector;
00494 
00495 // Copy ATA attributes to persistent state.
00496 void dev_state::update_persistent_state()
00497 {
00498   for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
00499     const ata_smart_attribute & ta = smartval.vendor_attributes[i];
00500     ata_attribute & pa = ata_attributes[i];
00501     pa.id = ta.id;
00502     if (ta.id == 0) {
00503       pa.val = pa.worst = 0; pa.raw = 0;
00504       continue;
00505     }
00506     pa.val = ta.current;
00507     pa.worst = ta.worst;
00508     pa.raw =            ta.raw[0]
00509            | (          ta.raw[1] <<  8)
00510            | (          ta.raw[2] << 16)
00511            | ((uint64_t)ta.raw[3] << 24)
00512            | ((uint64_t)ta.raw[4] << 32)
00513            | ((uint64_t)ta.raw[5] << 40);
00514     pa.resvd = ta.reserv;
00515   }
00516 }
00517 
00518 // Copy ATA from persistent to temp state.
00519 void dev_state::update_temp_state()
00520 {
00521   for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
00522     const ata_attribute & pa = ata_attributes[i];
00523     ata_smart_attribute & ta = smartval.vendor_attributes[i];
00524     ta.id = pa.id;
00525     if (pa.id == 0) {
00526       ta.current = ta.worst = 0;
00527       memset(ta.raw, 0, sizeof(ta.raw));
00528       continue;
00529     }
00530     ta.current = pa.val;
00531     ta.worst = pa.worst;
00532     ta.raw[0] = (unsigned char) pa.raw;
00533     ta.raw[1] = (unsigned char)(pa.raw >>  8);
00534     ta.raw[2] = (unsigned char)(pa.raw >> 16);
00535     ta.raw[3] = (unsigned char)(pa.raw >> 24);
00536     ta.raw[4] = (unsigned char)(pa.raw >> 32);
00537     ta.raw[5] = (unsigned char)(pa.raw >> 40);
00538     ta.reserv = pa.resvd;
00539   }
00540 }
00541 
00542 // Parse a line from a state file.
00543 static bool parse_dev_state_line(const char * line, persistent_dev_state & state)
00544 {
00545   static const regular_expression regex(
00546     "^ *"
00547      "((temperature-min)" // (1 (2)
00548      "|(temperature-max)" // (3)
00549      "|(self-test-errors)" // (4)
00550      "|(self-test-last-err-hour)" // (5)
00551      "|(scheduled-test-next-check)" // (6)
00552      "|(selective-test-last-start)" // (7)
00553      "|(selective-test-last-end)" // (8)
00554      "|(ata-error-count)"  // (9)
00555      "|(mail\\.([0-9]+)\\." // (10 (11)
00556        "((count)" // (12 (13)
00557        "|(first-sent-time)" // (14)
00558        "|(last-sent-time)" // (15)
00559        ")" // 12)
00560       ")" // 10)
00561      "|(ata-smart-attribute\\.([0-9]+)\\." // (16 (17)
00562        "((id)" // (18 (19)
00563        "|(val)" // (20)
00564        "|(worst)" // (21)
00565        "|(raw)" // (22)
00566        "|(resvd)" // (23)
00567        ")" // 18)
00568       ")" // 16)
00569      ")" // 1)
00570      " *= *([0-9]+)[ \n]*$", // (24)
00571     REG_EXTENDED
00572   );
00573 
00574   const int nmatch = 1+24;
00575   regmatch_t match[nmatch];
00576   if (!regex.execute(line, nmatch, match))
00577     return false;
00578   if (match[nmatch-1].rm_so < 0)
00579     return false;
00580 
00581   uint64_t val = strtoull(line + match[nmatch-1].rm_so, (char **)0, 10);
00582 
00583   int m = 1;
00584   if (match[++m].rm_so >= 0)
00585     state.tempmin = (unsigned char)val;
00586   else if (match[++m].rm_so >= 0)
00587     state.tempmax = (unsigned char)val;
00588   else if (match[++m].rm_so >= 0)
00589     state.selflogcount = (unsigned char)val;
00590   else if (match[++m].rm_so >= 0)
00591     state.selfloghour = (unsigned short)val;
00592   else if (match[++m].rm_so >= 0)
00593     state.scheduled_test_next_check = (time_t)val;
00594   else if (match[++m].rm_so >= 0)
00595     state.selective_test_last_start = val;
00596   else if (match[++m].rm_so >= 0)
00597     state.selective_test_last_end = val;
00598   else if (match[++m].rm_so >= 0)
00599     state.ataerrorcount = (int)val;
00600   else if (match[m+=2].rm_so >= 0) {
00601     int i = atoi(line+match[m].rm_so);
00602     if (!(0 <= i && i < SMARTD_NMAIL))
00603       return false;
00604     if (i == MAILTYPE_TEST) // Don't suppress test mails
00605       return true;
00606     if (match[m+=2].rm_so >= 0)
00607       state.maillog[i].logged = (int)val;
00608     else if (match[++m].rm_so >= 0)
00609       state.maillog[i].firstsent = (time_t)val;
00610     else if (match[++m].rm_so >= 0)
00611       state.maillog[i].lastsent = (time_t)val;
00612     else
00613       return false;
00614   }
00615   else if (match[m+=5+1].rm_so >= 0) {
00616     int i = atoi(line+match[m].rm_so);
00617     if (!(0 <= i && i < NUMBER_ATA_SMART_ATTRIBUTES))
00618       return false;
00619     if (match[m+=2].rm_so >= 0)
00620       state.ata_attributes[i].id = (unsigned char)val;
00621     else if (match[++m].rm_so >= 0)
00622       state.ata_attributes[i].val = (unsigned char)val;
00623     else if (match[++m].rm_so >= 0)
00624       state.ata_attributes[i].worst = (unsigned char)val;
00625     else if (match[++m].rm_so >= 0)
00626       state.ata_attributes[i].raw = val;
00627     else if (match[++m].rm_so >= 0)
00628       state.ata_attributes[i].resvd = (unsigned char)val;
00629     else
00630       return false;
00631   }
00632   else
00633     return false;
00634   return true;
00635 }
00636 
00637 // Read a state file.
00638 static bool read_dev_state(const char * path, persistent_dev_state & state)
00639 {
00640   stdio_file f(path, "r");
00641   if (!f) {
00642     if (errno != ENOENT)
00643       pout("Cannot read state file \"%s\"\n", path);
00644     return false;
00645   }
00646 #ifdef __CYGWIN__
00647   setmode(fileno(f), O_TEXT); // Allow files with \r\n
00648 #endif
00649 
00650   persistent_dev_state new_state;
00651   int good = 0, bad = 0;
00652   char line[256];
00653   while (fgets(line, sizeof(line), f)) {
00654     const char * s = line + strspn(line, " \t");
00655     if (!*s || *s == '#')
00656       continue;
00657     if (!parse_dev_state_line(line, new_state))
00658       bad++;
00659     else
00660       good++;
00661   }
00662 
00663   if (bad) {
00664     if (!good) {
00665       pout("%s: format error\n", path);
00666       return false;
00667     }
00668     pout("%s: %d invalid line(s) ignored\n", path, bad);
00669   }
00670 
00671   // This sets the values missing in the file to 0.
00672   state = new_state;
00673   return true;
00674 }
00675 
00676 static void write_dev_state_line(FILE * f, const char * name, uint64_t val)
00677 {
00678   if (val)
00679     fprintf(f, "%s = %" PRIu64 "\n", name, val);
00680 }
00681 
00682 static void write_dev_state_line(FILE * f, const char * name1, int id, const char * name2, uint64_t val)
00683 {
00684   if (val)
00685     fprintf(f, "%s.%d.%s = %" PRIu64 "\n", name1, id, name2, val);
00686 }
00687 
00688 // Write a state file
00689 static bool write_dev_state(const char * path, const persistent_dev_state & state)
00690 {
00691   // Rename old "file" to "file~"
00692   std::string pathbak = path; pathbak += '~';
00693   unlink(pathbak.c_str());
00694   rename(path, pathbak.c_str());
00695 
00696   stdio_file f(path, "w");
00697   if (!f) {
00698     pout("Cannot create state file \"%s\"\n", path);
00699     return false;
00700   }
00701 
00702   fprintf(f, "# smartd state file\n");
00703   write_dev_state_line(f, "temperature-min", state.tempmin);
00704   write_dev_state_line(f, "temperature-max", state.tempmax);
00705   write_dev_state_line(f, "self-test-errors", state.selflogcount);
00706   write_dev_state_line(f, "self-test-last-err-hour", state.selfloghour);
00707   write_dev_state_line(f, "scheduled-test-next-check", state.scheduled_test_next_check);
00708   write_dev_state_line(f, "selective-test-last-start", state.selective_test_last_start);
00709   write_dev_state_line(f, "selective-test-last-end", state.selective_test_last_end);
00710 
00711   int i;
00712   for (i = 0; i < SMARTD_NMAIL; i++) {
00713     if (i == MAILTYPE_TEST) // Don't suppress test mails
00714       continue;
00715     const mailinfo & mi = state.maillog[i];
00716     if (!mi.logged)
00717       continue;
00718     write_dev_state_line(f, "mail", i, "count", mi.logged);
00719     write_dev_state_line(f, "mail", i, "first-sent-time", mi.firstsent);
00720     write_dev_state_line(f, "mail", i, "last-sent-time", mi.lastsent);
00721   }
00722 
00723   // ATA ONLY
00724   write_dev_state_line(f, "ata-error-count", state.ataerrorcount);
00725 
00726   for (i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
00727     const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
00728     if (!pa.id)
00729       continue;
00730     write_dev_state_line(f, "ata-smart-attribute", i, "id", pa.id);
00731     write_dev_state_line(f, "ata-smart-attribute", i, "val", pa.val);
00732     write_dev_state_line(f, "ata-smart-attribute", i, "worst", pa.worst);
00733     write_dev_state_line(f, "ata-smart-attribute", i, "raw", pa.raw);
00734     write_dev_state_line(f, "ata-smart-attribute", i, "resvd", pa.resvd);
00735   }
00736 
00737   return true;
00738 }
00739 
00740 // Write to the attrlog file
00741 static bool write_dev_attrlog(const char * path, const dev_state & state)
00742 {
00743   stdio_file f(path, "a");
00744   if (!f) {
00745     pout("Cannot create attribute log file \"%s\"\n", path);
00746     return false;
00747   }
00748 
00749   
00750   time_t now = time(0);
00751   struct tm * tms = gmtime(&now);
00752   fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;",
00753              1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday,
00754              tms->tm_hour, tms->tm_min, tms->tm_sec);
00755   // ATA ONLY
00756   for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
00757     const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
00758     if (!pa.id)
00759       continue;
00760     fprintf(f, "\t%d;%d;%" PRIu64 ";", pa.id, pa.val, pa.raw);
00761   }
00762   // SCSI ONLY
00763   const struct scsiErrorCounter * ecp;
00764   const char * pageNames[3] = {"read", "write", "verify"};
00765   for (int k = 0; k < 3; ++k) {
00766     if ( !state.scsi_error_counters[k].found ) continue;
00767     ecp = &state.scsi_error_counters[k].errCounter;
00768      fprintf(f, "\t%s-corr-by-ecc-fast;%" PRIu64 ";"
00769        "\t%s-corr-by-ecc-delayed;%" PRIu64 ";"
00770        "\t%s-corr-by-retry;%" PRIu64 ";"
00771        "\t%s-total-err-corrected;%" PRIu64 ";"
00772        "\t%s-corr-algorithm-invocations;%" PRIu64 ";"
00773        "\t%s-gb-processed;%.3f;"
00774        "\t%s-total-unc-errors;%" PRIu64 ";",
00775        pageNames[k], ecp->counter[0],
00776        pageNames[k], ecp->counter[1],
00777        pageNames[k], ecp->counter[2],
00778        pageNames[k], ecp->counter[3],
00779        pageNames[k], ecp->counter[4],
00780        pageNames[k], (ecp->counter[5] / 1000000000.0),
00781        pageNames[k], ecp->counter[6]);
00782   }
00783   if(state.scsi_nonmedium_error.found && state.scsi_nonmedium_error.nme.gotPC0) {
00784     fprintf(f, "\tnon-medium-errors;%" PRIu64 ";", state.scsi_nonmedium_error.nme.counterPC0);
00785   }
00786   // write SCSI current temperature if it is monitored
00787   if(state.TempPageSupported && state.temperature)
00788      fprintf(f, "\ttemperature;%d;", state.temperature);
00789   // end of line
00790   fprintf(f, "\n");
00791   return true;
00792 }
00793 
00794 // Write all state files. If write_always is false, don't write
00795 // unless must_write is set.
00796 static void write_all_dev_states(const dev_config_vector & configs,
00797                                  dev_state_vector & states,
00798                                  bool write_always = true)
00799 {
00800   for (unsigned i = 0; i < states.size(); i++) {
00801     const dev_config & cfg = configs.at(i);
00802     if (cfg.state_file.empty())
00803       continue;
00804     dev_state & state = states[i];
00805     if (!write_always && !state.must_write)
00806       continue;
00807     if (!write_dev_state(cfg.state_file.c_str(), state))
00808       continue;
00809     state.must_write = false;
00810     if (write_always || debugmode)
00811       PrintOut(LOG_INFO, "Device: %s, state written to %s\n",
00812                cfg.name.c_str(), cfg.state_file.c_str());
00813   }
00814 }
00815 
00816 // Write to all attrlog files
00817 static void write_all_dev_attrlogs(const dev_config_vector & configs,
00818                                    dev_state_vector & states)
00819 {
00820   for (unsigned i = 0; i < states.size(); i++) {
00821     const dev_config & cfg = configs.at(i);
00822     if (cfg.attrlog_file.empty())
00823       continue;
00824     dev_state & state = states[i];
00825     write_dev_attrlog(cfg.attrlog_file.c_str(), state);
00826   }
00827 }
00828 
00829 // remove the PID file
00830 static void RemovePidFile()
00831 {
00832   if (!pid_file.empty()) {
00833     if (unlink(pid_file.c_str()))
00834       PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", 
00835                pid_file.c_str(), strerror(errno));
00836     pid_file.clear();
00837   }
00838   return;
00839 }
00840 
00841 extern "C" { // signal handlers require C-linkage
00842 
00843 //  Note if we catch a SIGUSR1
00844 static void USR1handler(int sig)
00845 {
00846   if (SIGUSR1==sig)
00847     caughtsigUSR1=1;
00848   return;
00849 }
00850 
00851 #ifdef _WIN32
00852 //  Note if we catch a SIGUSR2
00853 static void USR2handler(int sig)
00854 {
00855   if (SIGUSR2==sig)
00856     caughtsigUSR2=1;
00857   return;
00858 }
00859 #endif
00860 
00861 // Note if we catch a HUP (or INT in debug mode)
00862 static void HUPhandler(int sig)
00863 {
00864   if (sig==SIGHUP)
00865     caughtsigHUP=1;
00866   else
00867     caughtsigHUP=2;
00868   return;
00869 }
00870 
00871 // signal handler for TERM, QUIT, and INT (if not in debug mode)
00872 static void sighandler(int sig)
00873 {
00874   if (!caughtsigEXIT)
00875     caughtsigEXIT=sig;
00876   return;
00877 }
00878 
00879 } // extern "C"
00880 
00881 // Cleanup, print Goodbye message and remove pidfile
00882 static int Goodbye(int status)
00883 {
00884   // delete PID file, if one was created
00885   RemovePidFile();
00886 
00887   // if we are exiting because of a code bug, tell user
00888   if (status==EXIT_BADCODE)
00889         PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
00890 
00891   // and this should be the final output from smartd before it exits
00892   PrintOut(status?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", status);
00893 
00894   return status;
00895 }
00896 
00897 // a replacement for setenv() which is not available on all platforms.
00898 // Note that the string passed to putenv must not be freed or made
00899 // invalid, since a pointer to it is kept by putenv(). This means that
00900 // it must either be a static buffer or allocated off the heap. The
00901 // string can be freed if the environment variable is redefined via
00902 // another call to putenv(). There is no portable way to unset a variable
00903 // with putenv(). So we manage the buffer in a static object.
00904 // Using setenv() if available is not considered because some
00905 // implementations may produce memory leaks.
00906 
00907 class env_buffer
00908 {
00909 public:
00910   env_buffer()
00911     : m_buf((char *)0) { }
00912 
00913   void set(const char * name, const char * value);
00914 
00915 private:
00916   char * m_buf;
00917 
00918   env_buffer(const env_buffer &);
00919   void operator=(const env_buffer &);
00920 };
00921 
00922 void env_buffer::set(const char * name, const char * value)
00923 {
00924   int size = strlen(name) + 1 + strlen(value) + 1;
00925   char * newbuf = new char[size];
00926   snprintf(newbuf, size, "%s=%s", name, value);
00927 
00928   if (putenv(newbuf))
00929     throw std::runtime_error("putenv() failed");
00930 
00931   // This assumes that the same NAME is passed on each call
00932   delete [] m_buf;
00933   m_buf = newbuf;
00934 }
00935 
00936 #define EBUFLEN 1024
00937 
00938 static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
00939                         __attribute_format_printf(4, 5);
00940 
00941 // If either address or executable path is non-null then send and log
00942 // a warning email, or execute executable
00943 static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
00944 {
00945   static const char * const whichfail[] = {
00946     "EmailTest",                  // 0
00947     "Health",                     // 1
00948     "Usage",                      // 2
00949     "SelfTest",                   // 3
00950     "ErrorCount",                 // 4
00951     "FailedHealthCheck",          // 5
00952     "FailedReadSmartData",        // 6
00953     "FailedReadSmartErrorLog",    // 7
00954     "FailedReadSmartSelfTestLog", // 8
00955     "FailedOpenDevice",           // 9
00956     "CurrentPendingSector",       // 10
00957     "OfflineUncorrectableSector", // 11
00958     "Temperature"                 // 12
00959   };
00960   
00961   // See if user wants us to send mail
00962   if (cfg.emailaddress.empty() && cfg.emailcmdline.empty())
00963     return;
00964 
00965   std::string address = cfg.emailaddress;
00966   const char * executable = cfg.emailcmdline.c_str();
00967 
00968   // which type of mail are we sending?
00969   mailinfo * mail=(state.maillog)+which;
00970 
00971   // checks for sanity
00972   if (cfg.emailfreq<1 || cfg.emailfreq>3) {
00973     PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg.mailwarn->emailfreq=%d\n",cfg.emailfreq);
00974     return;
00975   }
00976   if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
00977     PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n",
00978              which, (int)sizeof(whichfail));
00979     return;
00980   }
00981 
00982   // Return if a single warning mail has been sent.
00983   if ((cfg.emailfreq==1) && mail->logged)
00984     return;
00985 
00986   // Return if this is an email test and one has already been sent.
00987   if (which == 0 && mail->logged)
00988     return;
00989   
00990   // To decide if to send mail, we need to know what time it is.
00991   time_t epoch = time(0);
00992 
00993   // Return if less than one day has gone by
00994   const int day = 24*3600;
00995   if (cfg.emailfreq==2 && mail->logged && epoch<(mail->lastsent+day))
00996     return;
00997 
00998   // Return if less than 2^(logged-1) days have gone by
00999   if (cfg.emailfreq==3 && mail->logged) {
01000     int days = 0x01 << (mail->logged - 1);
01001     days*=day;
01002     if  (epoch<(mail->lastsent+days))
01003       return;
01004   }
01005 
01006 #ifdef HAVE_LIBCAP_NG
01007   if (enable_capabilities) {
01008     PrintOut(LOG_ERR, "Sending a mail was supressed. "
01009              "Mails can't be send when capabilites are enabled\n");
01010     return;
01011   }
01012 #endif
01013 
01014   // record the time of this mail message, and the first mail message
01015   if (!mail->logged)
01016     mail->firstsent=epoch;
01017   mail->lastsent=epoch;
01018 
01019   // print warning string into message
01020   char message[256];
01021   va_list ap;
01022   va_start(ap, fmt);
01023   vsnprintf(message, sizeof(message), fmt, ap);
01024   va_end(ap);
01025 
01026   // replace commas by spaces to separate recipients
01027   std::replace(address.begin(), address.end(), ',', ' ');
01028 
01029   // Export information in environment variables that will be useful
01030   // for user scripts
01031   static env_buffer env[12];
01032   env[0].set("SMARTD_MAILER", executable);
01033   env[1].set("SMARTD_MESSAGE", message);
01034   char dates[DATEANDEPOCHLEN];
01035   snprintf(dates, sizeof(dates), "%d", mail->logged);
01036   env[2].set("SMARTD_PREVCNT", dates);
01037   dateandtimezoneepoch(dates, mail->firstsent);
01038   env[3].set("SMARTD_TFIRST", dates);
01039   snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent);
01040   env[4].set("SMARTD_TFIRSTEPOCH", dates);
01041   env[5].set("SMARTD_FAILTYPE", whichfail[which]);
01042   env[6].set("SMARTD_ADDRESS", address.c_str());
01043   env[7].set("SMARTD_DEVICESTRING", cfg.name.c_str());
01044 
01045   // Allow 'smartctl ... -d $SMARTD_DEVICETYPE $SMARTD_DEVICE'
01046   env[8].set("SMARTD_DEVICETYPE",
01047              (!cfg.dev_type.empty() ? cfg.dev_type.c_str() : "auto"));
01048   env[9].set("SMARTD_DEVICE", cfg.dev_name.c_str());
01049 
01050   env[10].set("SMARTD_DEVICEINFO", cfg.dev_idinfo.c_str());
01051   dates[0] = 0;
01052   if (which) switch (cfg.emailfreq) {
01053     case 2: dates[0] = '1'; dates[1] = 0; break;
01054     case 3: snprintf(dates, sizeof(dates), "%d", (0x01)<<mail->logged);
01055   }
01056   env[11].set("SMARTD_NEXTDAYS", dates);
01057 
01058   // now construct a command to send this as EMAIL
01059   char command[2048];
01060   if (!*executable)
01061     executable = "<mail>";
01062   const char * newadd = (!address.empty()? address.c_str() : "<nomailer>");
01063   const char * newwarn = (which? "Warning via" : "Test of");
01064 
01065 #ifndef _WIN32
01066   snprintf(command, sizeof(command), "%s 2>&1", warning_script.c_str());
01067   
01068   // tell SYSLOG what we are about to do...
01069   PrintOut(LOG_INFO,"%s %s to %s ...\n",
01070            which?"Sending warning via":"Executing test of", executable, newadd);
01071   
01072   // issue the command to send mail or to run the user's executable
01073   errno=0;
01074   FILE * pfp;
01075   if (!(pfp=popen(command, "r")))
01076     // failed to popen() mail process
01077     PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", 
01078              newwarn,  executable, newadd, errno?strerror(errno):"");
01079   else {
01080     // pipe suceeded!
01081     int len, status;
01082     char buffer[EBUFLEN];
01083 
01084     // if unexpected output on stdout/stderr, null terminate, print, and flush
01085     if ((len=fread(buffer, 1, EBUFLEN, pfp))) {
01086       int count=0;
01087       int newlen = len<EBUFLEN ? len : EBUFLEN-1;
01088       buffer[newlen]='\0';
01089       PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n", 
01090                newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer);
01091       
01092       // flush pipe if needed
01093       while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN)
01094         count++;
01095 
01096       // tell user that pipe was flushed, or that something is really wrong
01097       if (count && count<EBUFLEN)
01098         PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n", 
01099                  newwarn, executable, newadd);
01100       else if (count)
01101         PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n", 
01102                  newwarn, executable, newadd);
01103     }
01104     
01105     // if something went wrong with mail process, print warning
01106     errno=0;
01107     if (-1==(status=pclose(pfp)))
01108       PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd,
01109                errno?strerror(errno):"");
01110     else {
01111       // mail process apparently succeeded. Check and report exit status
01112       int status8;
01113 
01114       if (WIFEXITED(status)) {
01115         // exited 'normally' (but perhaps with nonzero status)
01116         status8=WEXITSTATUS(status);
01117         
01118         if (status8>128)  
01119           PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", 
01120                    newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128));
01121         else if (status8)  
01122           PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", 
01123                    newwarn, executable, newadd, status, status8);
01124         else
01125           PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
01126       }
01127       
01128       if (WIFSIGNALED(status))
01129         PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", 
01130                  newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status)));
01131       
01132       // this branch is probably not possible. If subprocess is
01133       // stopped then pclose() should not return.
01134       if (WIFSTOPPED(status)) 
01135         PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n",
01136                  newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status)));
01137       
01138     }
01139   }
01140   
01141 #else // _WIN32
01142   {
01143     snprintf(command, sizeof(command), "cmd /c \"%s\"", warning_script.c_str());
01144 
01145     char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog()
01146     int rc;
01147     // run command
01148     PrintOut(LOG_INFO,"%s %s to %s ...\n",
01149              (which?"Sending warning via":"Executing test of"), executable, newadd);
01150     rc = daemon_spawn(command, "", 0, stdoutbuf, sizeof(stdoutbuf));
01151     if (rc >= 0 && stdoutbuf[0])
01152       PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n",
01153         newwarn, executable, newadd, (int)strlen(stdoutbuf), stdoutbuf);
01154     if (rc != 0)
01155       PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n",
01156         newwarn, executable, newadd, rc);
01157     else
01158       PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
01159   }
01160 
01161 #endif // _WIN32
01162 
01163   // increment mail sent counter
01164   mail->logged++;
01165 }
01166 
01167 static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
01168                                __attribute_format_printf(4, 5);
01169 
01170 static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
01171 {
01172   if (!(0 <= which && which < SMARTD_NMAIL))
01173     return;
01174 
01175   // Return if no mail sent yet
01176   mailinfo & mi = state.maillog[which];
01177   if (!mi.logged)
01178     return;
01179 
01180   // Format & print message
01181   char msg[256];
01182   va_list ap;
01183   va_start(ap, fmt);
01184   vsnprintf(msg, sizeof(msg), fmt, ap);
01185   va_end(ap);
01186 
01187   PrintOut(LOG_INFO, "Device: %s, %s, warning condition reset after %d email%s\n", cfg.name.c_str(),
01188            msg, mi.logged, (mi.logged==1 ? "" : "s"));
01189 
01190   // Clear mail counter and timestamps
01191   mi = mailinfo();
01192   state.must_write = true;
01193 }
01194 
01195 #ifndef _WIN32
01196 
01197 // Output multiple lines via separate syslog(3) calls.
01198 static void vsyslog_lines(int priority, const char * fmt, va_list ap)
01199 {
01200   char buf[512+EBUFLEN]; // enough space for exec cmd output in MailWarning()
01201   vsnprintf(buf, sizeof(buf), fmt, ap);
01202 
01203   for (char * p = buf, * q; p && *p; p = q) {
01204     if ((q = strchr(p, '\n')))
01205       *q++ = 0;
01206     if (*p)
01207       syslog(priority, "%s\n", p);
01208   }
01209 }
01210 
01211 #else  // _WIN32
01212 // os_win32/syslog_win32.cpp supports multiple lines.
01213 #define vsyslog_lines vsyslog
01214 #endif // _WIN32
01215 
01216 // Printing function for watching ataprint commands, or losing them
01217 // [From GLIBC Manual: Since the prototype doesn't specify types for
01218 // optional arguments, in a call to a variadic function the default
01219 // argument promotions are performed on the optional argument
01220 // values. This means the objects of type char or short int (whether
01221 // signed or not) are promoted to either int or unsigned int, as
01222 // appropriate.]
01223 void pout(const char *fmt, ...){
01224   va_list ap;
01225 
01226   // get the correct time in syslog()
01227   FixGlibcTimeZoneBug();
01228   // initialize variable argument list 
01229   va_start(ap,fmt);
01230   // in debugmode==1 mode we will print the output from the ataprint.o functions!
01231   if (debugmode && debugmode != 2) {
01232     FILE * f = stdout;
01233 #ifdef _WIN32
01234     if (facility == LOG_LOCAL1) // logging to stdout
01235       f = stderr;
01236 #endif
01237     vfprintf(f, fmt, ap);
01238     fflush(f);
01239   }
01240   // in debugmode==2 mode we print output from knowndrives.o functions
01241   else if (debugmode==2 || ata_debugmode || scsi_debugmode) {
01242     openlog("smartd", LOG_PID, facility);
01243     vsyslog_lines(LOG_INFO, fmt, ap);
01244     closelog();
01245   }
01246   va_end(ap);
01247   return;
01248 }
01249 
01250 // This function prints either to stdout or to the syslog as needed.
01251 static void PrintOut(int priority, const char *fmt, ...){
01252   va_list ap;
01253   
01254   // get the correct time in syslog()
01255   FixGlibcTimeZoneBug();
01256   // initialize variable argument list 
01257   va_start(ap,fmt);
01258   if (debugmode) {
01259     FILE * f = stdout;
01260 #ifdef _WIN32
01261     if (facility == LOG_LOCAL1) // logging to stdout
01262       f = stderr;
01263 #endif
01264     vfprintf(f, fmt, ap);
01265     fflush(f);
01266   }
01267   else {
01268     openlog("smartd", LOG_PID, facility);
01269     vsyslog_lines(priority, fmt, ap);
01270     closelog();
01271   }
01272   va_end(ap);
01273   return;
01274 }
01275 
01276 // Used to warn users about invalid checksums. Called from atacmds.cpp.
01277 void checksumwarning(const char * string)
01278 {
01279   pout("Warning! %s error: invalid SMART checksum.\n", string);
01280 }
01281 
01282 #ifndef _WIN32
01283 
01284 // Wait for the pid file to show up, this makes sure a calling program knows
01285 // that the daemon is really up and running and has a pid to kill it
01286 static bool WaitForPidFile()
01287 {
01288     int waited, max_wait = 10;
01289     struct stat stat_buf;
01290 
01291     if (pid_file.empty() || debugmode)
01292         return true;
01293 
01294     for(waited = 0; waited < max_wait; ++waited) {
01295         if (!stat(pid_file.c_str(), &stat_buf)) {
01296                 return true;
01297         } else
01298                 sleep(1);
01299     }
01300     return false;
01301 }
01302 
01303 #endif // _WIN32
01304 
01305 // Forks new process, closes ALL file descriptors, redirects stdin,
01306 // stdout, and stderr.  Not quite daemon().  See
01307 // http://www.linuxjournal.com/article/2335
01308 // for a good description of why we do things this way.
01309 static void DaemonInit()
01310 {
01311 #ifndef _WIN32
01312   pid_t pid;
01313   int i;  
01314 
01315   // flush all buffered streams.  Else we might get two copies of open
01316   // streams since both parent and child get copies of the buffers.
01317   fflush(NULL);
01318 
01319   if (do_fork) {
01320     if ((pid=fork()) < 0) {
01321       // unable to fork!
01322       PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
01323       EXIT(EXIT_STARTUP);
01324     }
01325     else if (pid) {
01326       // we are the parent process, wait for pid file, then exit cleanly
01327       if(!WaitForPidFile()) {
01328         PrintOut(LOG_CRIT,"PID file %s didn't show up!\n", pid_file.c_str());
01329         EXIT(EXIT_STARTUP);
01330       } else
01331         EXIT(0);
01332     }
01333   
01334     // from here on, we are the child process.
01335     setsid();
01336 
01337     // Fork one more time to avoid any possibility of having terminals
01338     if ((pid=fork()) < 0) {
01339       // unable to fork!
01340       PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
01341       EXIT(EXIT_STARTUP);
01342     }
01343     else if (pid)
01344       // we are the parent process -- exit cleanly
01345       EXIT(0);
01346 
01347     // Now we are the child's child...
01348   }
01349 
01350   // close any open file descriptors
01351   for (i=getdtablesize();i>=0;--i)
01352     close(i);
01353   
01354 #define NO_warn_unused_result(cmd) { if (cmd) {} ; }
01355 
01356   // redirect any IO attempts to /dev/null for stdin
01357   i=open("/dev/null",O_RDWR);
01358   if (i>=0) {
01359     // stdout
01360     NO_warn_unused_result(dup(i));
01361     // stderr
01362     NO_warn_unused_result(dup(i));
01363   };
01364   umask(0022);
01365   NO_warn_unused_result(chdir("/"));
01366 
01367   if (do_fork)
01368     PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());
01369 
01370 #else // _WIN32
01371 
01372   // No fork() on native Win32
01373   // Detach this process from console
01374   fflush(NULL);
01375   if (daemon_detach("smartd")) {
01376     PrintOut(LOG_CRIT,"smartd unable to detach from console!\n");
01377     EXIT(EXIT_STARTUP);
01378   }
01379   // stdin/out/err now closed if not redirected
01380 
01381 #endif // _WIN32
01382   return;
01383 }
01384 
01385 // create a PID file containing the current process id
01386 static void WritePidFile()
01387 {
01388   if (!pid_file.empty()) {
01389     pid_t pid = getpid();
01390     mode_t old_umask;
01391 #ifndef __CYGWIN__
01392     old_umask = umask(0077); // rwx------
01393 #else
01394     // Cygwin: smartd service runs on system account, ensure PID file can be read by admins
01395     old_umask = umask(0033); // rwxr--r--
01396 #endif
01397 
01398     stdio_file f(pid_file.c_str(), "w");
01399     umask(old_umask);
01400     if (!(f && fprintf(f, "%d\n", (int)pid) > 0 && f.close())) {
01401       PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file.c_str());
01402       EXIT(EXIT_PID);
01403     }
01404     PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file.c_str(), (int)pid);
01405   }
01406 }
01407 
01408 // Prints header identifying version of code and home
01409 static void PrintHead()
01410 {
01411   PrintOut(LOG_INFO, "%s\n", format_version_info("smartd").c_str());
01412 }
01413 
01414 // prints help info for configuration file Directives
01415 static void Directives()
01416 {
01417   PrintOut(LOG_INFO,
01418            "Configuration file (%s) Directives (after device name):\n"
01419            "  -d TYPE Set the device type: auto, ignore, removable,\n"
01420            "          %s\n"
01421            "  -T TYPE Set the tolerance to one of: normal, permissive\n"
01422            "  -o VAL  Enable/disable automatic offline tests (on/off)\n"
01423            "  -S VAL  Enable/disable attribute autosave (on/off)\n"
01424            "  -n MODE No check if: never, sleep[,N][,q], standby[,N][,q], idle[,N][,q]\n"
01425            "  -H      Monitor SMART Health Status, report if failed\n"
01426            "  -s REG  Do Self-Test at time(s) given by regular expression REG\n"
01427            "  -l TYPE Monitor SMART log or self-test status:\n"
01428            "          error, selftest, xerror, offlinests[,ns], selfteststs[,ns]\n"
01429            "  -l scterc,R,W  Set SCT Error Recovery Control\n"
01430            "  -e      Change device setting: aam,[N|off], apm,[N|off], lookahead,[on|off],\n"
01431            "          security-freeze, standby,[N|off], wcache,[on|off]\n"
01432            "  -f      Monitor 'Usage' Attributes, report failures\n"
01433            "  -m ADD  Send email warning to address ADD\n"
01434            "  -M TYPE Modify email warning behavior (see man page)\n"
01435            "  -p      Report changes in 'Prefailure' Attributes\n"
01436            "  -u      Report changes in 'Usage' Attributes\n"
01437            "  -t      Equivalent to -p and -u Directives\n"
01438            "  -r ID   Also report Raw values of Attribute ID with -p, -u or -t\n"
01439            "  -R ID   Track changes in Attribute ID Raw value with -p, -u or -t\n"
01440            "  -i ID   Ignore Attribute ID for -f Directive\n"
01441            "  -I ID   Ignore Attribute ID for -p, -u or -t Directive\n"
01442            "  -C ID[+] Monitor [increases of] Current Pending Sectors in Attribute ID\n"
01443            "  -U ID[+] Monitor [increases of] Offline Uncorrectable Sectors in Attribute ID\n"
01444            "  -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit\n"
01445            "  -v N,ST Modifies labeling of Attribute N (see man page)  \n"
01446            "  -P TYPE Drive-specific presets: use, ignore, show, showall\n"
01447            "  -a      Default: -H -f -t -l error -l selftest -l selfteststs -C 197 -U 198\n"
01448            "  -F TYPE Use firmware bug workaround:\n"
01449            "          %s\n"
01450            "   #      Comment: text after a hash sign is ignored\n"
01451            "   \\      Line continuation character\n"
01452            "Attribute ID is a decimal integer 1 <= ID <= 255\n"
01453            "Use ID = 0 to turn off -C and/or -U Directives\n"
01454            "Example: /dev/sda -a\n",
01455            configfile,
01456            smi()->get_valid_dev_types_str().c_str(),
01457            get_valid_firmwarebug_args());
01458 }
01459 
01460 /* Returns a pointer to a static string containing a formatted list of the valid
01461    arguments to the option opt or NULL on failure. */
01462 static const char *GetValidArgList(char opt)
01463 {
01464   switch (opt) {
01465   case 'A':
01466   case 's':
01467     return "<PATH_PREFIX>";
01468   case 'c':
01469     return "<FILE_NAME>, -";
01470   case 'l':
01471     return "daemon, local0, local1, local2, local3, local4, local5, local6, local7";
01472   case 'q':
01473     return "nodev, errors, nodevstartup, never, onecheck, showtests";
01474   case 'r':
01475     return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
01476   case 'B':
01477   case 'p':
01478   case 'w':
01479     return "<FILE_NAME>";
01480   case 'i':
01481     return "<INTEGER_SECONDS>";
01482   default:
01483     return NULL;
01484   }
01485 }
01486 
01487 /* prints help information for command syntax */
01488 static void Usage()
01489 {
01490   PrintOut(LOG_INFO,"Usage: smartd [options]\n\n");
01491   PrintOut(LOG_INFO,"  -A PREFIX, --attributelog=PREFIX\n");
01492   PrintOut(LOG_INFO,"        Log ATA attribute information to {PREFIX}MODEL-SERIAL.ata.csv\n");
01493 #ifdef SMARTMONTOOLS_ATTRIBUTELOG
01494   PrintOut(LOG_INFO,"        [default is " SMARTMONTOOLS_ATTRIBUTELOG "MODEL-SERIAL.ata.csv]\n");
01495 #endif
01496   PrintOut(LOG_INFO,"\n");
01497   PrintOut(LOG_INFO,"  -B [+]FILE, --drivedb=[+]FILE\n");
01498   PrintOut(LOG_INFO,"        Read and replace [add] drive database from FILE\n");
01499   PrintOut(LOG_INFO,"        [default is +%s", get_drivedb_path_add());
01500 #ifdef SMARTMONTOOLS_DRIVEDBDIR
01501   PrintOut(LOG_INFO,"\n");
01502   PrintOut(LOG_INFO,"         and then    %s", get_drivedb_path_default());
01503 #endif
01504   PrintOut(LOG_INFO,"]\n\n");
01505   PrintOut(LOG_INFO,"  -c NAME|-, --configfile=NAME|-\n");
01506   PrintOut(LOG_INFO,"        Read configuration file NAME or stdin\n");
01507   PrintOut(LOG_INFO,"        [default is %s]\n\n", configfile);
01508 #ifdef HAVE_LIBCAP_NG
01509   PrintOut(LOG_INFO,"  -C, --capabilities\n");
01510   PrintOut(LOG_INFO,"        Use capabilities.\n"
01511                     "        Warning: Mail notification does not work when used.\n\n");
01512 #endif
01513   PrintOut(LOG_INFO,"  -d, --debug\n");
01514   PrintOut(LOG_INFO,"        Start smartd in debug mode\n\n");
01515   PrintOut(LOG_INFO,"  -D, --showdirectives\n");
01516   PrintOut(LOG_INFO,"        Print the configuration file Directives and exit\n\n");
01517   PrintOut(LOG_INFO,"  -h, --help, --usage\n");
01518   PrintOut(LOG_INFO,"        Display this help and exit\n\n");
01519   PrintOut(LOG_INFO,"  -i N, --interval=N\n");
01520   PrintOut(LOG_INFO,"        Set interval between disk checks to N seconds, where N >= 10\n\n");
01521   PrintOut(LOG_INFO,"  -l local[0-7], --logfacility=local[0-7]\n");
01522 #ifndef _WIN32
01523   PrintOut(LOG_INFO,"        Use syslog facility local0 - local7 or daemon [default]\n\n");
01524 #else
01525   PrintOut(LOG_INFO,"        Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n");
01526 #endif
01527 #ifndef _WIN32
01528   PrintOut(LOG_INFO,"  -n, --no-fork\n");
01529   PrintOut(LOG_INFO,"        Do not fork into background\n\n");
01530 #endif  // _WIN32
01531   PrintOut(LOG_INFO,"  -p NAME, --pidfile=NAME\n");
01532   PrintOut(LOG_INFO,"        Write PID file NAME\n\n");
01533   PrintOut(LOG_INFO,"  -q WHEN, --quit=WHEN\n");
01534   PrintOut(LOG_INFO,"        Quit on one of: %s\n\n", GetValidArgList('q'));
01535   PrintOut(LOG_INFO,"  -r, --report=TYPE\n");
01536   PrintOut(LOG_INFO,"        Report transactions for one of: %s\n\n", GetValidArgList('r'));
01537   PrintOut(LOG_INFO,"  -s PREFIX, --savestates=PREFIX\n");
01538   PrintOut(LOG_INFO,"        Save disk states to {PREFIX}MODEL-SERIAL.TYPE.state\n");
01539 #ifdef SMARTMONTOOLS_SAVESTATES
01540   PrintOut(LOG_INFO,"        [default is " SMARTMONTOOLS_SAVESTATES "MODEL-SERIAL.TYPE.state]\n");
01541 #endif
01542   PrintOut(LOG_INFO,"\n");
01543   PrintOut(LOG_INFO,"  -w NAME, --warnexec=NAME\n");
01544   PrintOut(LOG_INFO,"        Run executable NAME on warnings\n");
01545 #ifndef _WIN32
01546   PrintOut(LOG_INFO,"        [default is " SMARTMONTOOLS_SYSCONFDIR "/smartd_warning.sh]\n\n");
01547 #else
01548   PrintOut(LOG_INFO,"        [default is %s/smartd_warning.cmd]\n\n", get_exe_dir().c_str());
01549 #endif
01550 #ifdef _WIN32
01551   PrintOut(LOG_INFO,"  --service\n");
01552   PrintOut(LOG_INFO,"        Running as windows service (see man page), install with:\n");
01553   PrintOut(LOG_INFO,"          smartd install [options]\n");
01554   PrintOut(LOG_INFO,"        Remove service with:\n");
01555   PrintOut(LOG_INFO,"          smartd remove\n\n");
01556 #endif // _WIN32
01557   PrintOut(LOG_INFO,"  -V, --version, --license, --copyright\n");
01558   PrintOut(LOG_INFO,"        Print License, Copyright, and version information\n");
01559 }
01560 
01561 static int CloseDevice(smart_device * device, const char * name)
01562 {
01563   if (!device->close()){
01564     PrintOut(LOG_INFO,"Device: %s, %s, close() failed\n", name, device->get_errmsg());
01565     return 1;
01566   }
01567   // device sucessfully closed
01568   return 0;
01569 }
01570 
01571 // return true if a char is not allowed in a state file name
01572 static bool not_allowed_in_filename(char c)
01573 {
01574   return !(   ('0' <= c && c <= '9')
01575            || ('A' <= c && c <= 'Z')
01576            || ('a' <= c && c <= 'z'));
01577 }
01578 
01579 // Read error count from Summary or Extended Comprehensive SMART error log
01580 // Return -1 on error
01581 static int read_ata_error_count(ata_device * device, const char * name,
01582                                 firmwarebug_defs firmwarebugs, bool extended)
01583 {
01584   if (!extended) {
01585     ata_smart_errorlog log;
01586     if (ataReadErrorLog(device, &log, firmwarebugs)){
01587       PrintOut(LOG_INFO,"Device: %s, Read Summary SMART Error Log failed\n",name);
01588       return -1;
01589     }
01590     return (log.error_log_pointer ? log.ata_error_count : 0);
01591   }
01592   else {
01593     ata_smart_exterrlog logx;
01594     if (!ataReadExtErrorLog(device, &logx, 1 /*first sector only*/, firmwarebugs)) {
01595       PrintOut(LOG_INFO,"Device: %s, Read Extended Comprehensive SMART Error Log failed\n",name);
01596       return -1;
01597     }
01598     // Some disks use the reserved byte as index, see ataprint.cpp.
01599     return (logx.error_log_index || logx.reserved1 ? logx.device_error_count : 0);
01600   }
01601 }
01602 
01603 // returns <0 if problem.  Otherwise, bottom 8 bits are the self test
01604 // error count, and top bits are the power-on hours of the last error.
01605 static int SelfTestErrorCount(ata_device * device, const char * name,
01606                               firmwarebug_defs firmwarebugs)
01607 {
01608   struct ata_smart_selftestlog log;
01609 
01610   if (ataReadSelfTestLog(device, &log, firmwarebugs)){
01611     PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
01612     return -1;
01613   }
01614   
01615   // return current number of self-test errors
01616   return ataPrintSmartSelfTestlog(&log, false, firmwarebugs);
01617 }
01618 
01619 #define SELFTEST_ERRORCOUNT(x) (x & 0xff)
01620 #define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff)
01621 
01622 // Check offline data collection status
01623 static inline bool is_offl_coll_in_progress(unsigned char status)
01624 {
01625   return ((status & 0x7f) == 0x03);
01626 }
01627 
01628 // Check self-test execution status
01629 static inline bool is_self_test_in_progress(unsigned char status)
01630 {
01631   return ((status >> 4) == 0xf);
01632 }
01633 
01634 // Log offline data collection status
01635 static void log_offline_data_coll_status(const char * name, unsigned char status)
01636 {
01637   const char * msg;
01638   switch (status & 0x7f) {
01639     case 0x00: msg = "was never started"; break;
01640     case 0x02: msg = "was completed without error"; break;
01641     case 0x03: msg = "is in progress"; break;
01642     case 0x04: msg = "was suspended by an interrupting command from host"; break;
01643     case 0x05: msg = "was aborted by an interrupting command from host"; break;
01644     case 0x06: msg = "was aborted by the device with a fatal error"; break;
01645     default:   msg = 0;
01646   }
01647 
01648   if (msg)
01649     PrintOut(((status & 0x7f) == 0x06 ? LOG_CRIT : LOG_INFO),
01650              "Device: %s, offline data collection %s%s\n", name, msg,
01651              ((status & 0x80) ? " (auto:on)" : ""));
01652   else
01653     PrintOut(LOG_INFO, "Device: %s, unknown offline data collection status 0x%02x\n",
01654              name, status);
01655 }
01656 
01657 // Log self-test execution status
01658 static void log_self_test_exec_status(const char * name, unsigned char status)
01659 {
01660   const char * msg;
01661   switch (status >> 4) {
01662     case 0x0: msg = "completed without error"; break;
01663     case 0x1: msg = "was aborted by the host"; break;
01664     case 0x2: msg = "was interrupted by the host with a reset"; break;
01665     case 0x3: msg = "could not complete due to a fatal or unknown error"; break;
01666     case 0x4: msg = "completed with error (unknown test element)"; break;
01667     case 0x5: msg = "completed with error (electrical test element)"; break;
01668     case 0x6: msg = "completed with error (servo/seek test element)"; break;
01669     case 0x7: msg = "completed with error (read test element)"; break;
01670     case 0x8: msg = "completed with error (handling damage?)"; break;
01671     default:  msg = 0;
01672   }
01673 
01674   if (msg)
01675     PrintOut(((status >> 4) >= 0x4 ? LOG_CRIT : LOG_INFO),
01676              "Device: %s, previous self-test %s\n", name, msg);
01677   else if ((status >> 4) == 0xf)
01678     PrintOut(LOG_INFO, "Device: %s, self-test in progress, %u0%% remaining\n",
01679              name, status & 0x0f);
01680   else
01681     PrintOut(LOG_INFO, "Device: %s, unknown self-test status 0x%02x\n",
01682              name, status);
01683 }
01684 
01685 // Check pending sector count id (-C, -U directives).
01686 static bool check_pending_id(const dev_config & cfg, const dev_state & state,
01687                              unsigned char id, const char * msg)
01688 {
01689   // Check attribute index
01690   int i = ata_find_attr_index(id, state.smartval);
01691   if (i < 0) {
01692     PrintOut(LOG_INFO, "Device: %s, can't monitor %s count - no Attribute %d\n",
01693              cfg.name.c_str(), msg, id);
01694     return false;
01695   }
01696 
01697   // Check value
01698   uint64_t rawval = ata_get_attr_raw_value(state.smartval.vendor_attributes[i],
01699     cfg.attribute_defs);
01700   if (rawval >= (state.num_sectors ? state.num_sectors : 0xffffffffULL)) {
01701     PrintOut(LOG_INFO, "Device: %s, ignoring %s count - bogus Attribute %d value %" PRIu64 " (0x%" PRIx64 ")\n",
01702              cfg.name.c_str(), msg, id, rawval, rawval);
01703     return false;
01704   }
01705 
01706   return true;
01707 }
01708 
01709 // Called by ATA/SCSIDeviceScan() after successful device check
01710 static void finish_device_scan(dev_config & cfg, dev_state & state)
01711 {
01712   // Set cfg.emailfreq if user hasn't set it
01713   if ((!cfg.emailaddress.empty() || !cfg.emailcmdline.empty()) && !cfg.emailfreq) {
01714     // Avoid that emails are suppressed forever due to state persistence
01715     if (cfg.state_file.empty())
01716       cfg.emailfreq = 1; // '-M once'
01717     else
01718       cfg.emailfreq = 2; // '-M daily'
01719   }
01720 
01721   // Start self-test regex check now if time was not read from state file
01722   if (!cfg.test_regex.empty() && !state.scheduled_test_next_check)
01723     state.scheduled_test_next_check = time(0);
01724 }
01725 
01726 // Common function to format result message for ATA setting
01727 static void format_set_result_msg(std::string & msg, const char * name, bool ok,
01728                                   int set_option = 0, bool has_value = false)
01729 {
01730   if (!msg.empty())
01731     msg += ", ";
01732   msg += name;
01733   if (!ok)
01734     msg += ":--";
01735   else if (set_option < 0)
01736     msg += ":off";
01737   else if (has_value)
01738     msg += strprintf(":%d", set_option-1);
01739   else if (set_option > 0)
01740     msg += ":on";
01741 }
01742 
01743 
01744 // TODO: Add '-F swapid' directive
01745 const bool fix_swapped_id = false;
01746 
01747 // scan to see what ata devices there are, and if they support SMART
01748 static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atadev)
01749 {
01750   int supported=0;
01751   struct ata_identify_device drive;
01752   const char *name = cfg.name.c_str();
01753   int retid;
01754 
01755   // Device must be open
01756 
01757   // Get drive identity structure
01758   if ((retid = ata_read_identity(atadev, &drive, fix_swapped_id))) {
01759     if (retid<0)
01760       // Unable to read Identity structure
01761       PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name);
01762     else
01763       PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n",
01764                name, packetdevicetype(retid-1));
01765     CloseDevice(atadev, name);
01766     return 2; 
01767   }
01768 
01769   // Get drive identity, size and rotation rate (HDD/SSD)
01770   char model[40+1], serial[20+1], firmware[8+1];
01771   ata_format_id_string(model, drive.model, sizeof(model)-1);
01772   ata_format_id_string(serial, drive.serial_no, sizeof(serial)-1);
01773   ata_format_id_string(firmware, drive.fw_rev, sizeof(firmware)-1);
01774 
01775   ata_size_info sizes;
01776   ata_get_size_info(&drive, sizes);
01777   state.num_sectors = sizes.sectors;
01778   cfg.dev_rpm = ata_get_rotation_rate(&drive);
01779 
01780   char wwn[30]; wwn[0] = 0;
01781   unsigned oui = 0; uint64_t unique_id = 0;
01782   int naa = ata_get_wwn(&drive, oui, unique_id);
01783   if (naa >= 0)
01784     snprintf(wwn, sizeof(wwn), "WWN:%x-%06x-%09" PRIx64 ", ", naa, oui, unique_id);
01785 
01786   // Format device id string for warning emails
01787   char cap[32];
01788   cfg.dev_idinfo = strprintf("%s, S/N:%s, %sFW:%s, %s", model, serial, wwn, firmware,
01789                      format_capacity(cap, sizeof(cap), sizes.capacity, "."));
01790 
01791   PrintOut(LOG_INFO, "Device: %s, %s\n", name, cfg.dev_idinfo.c_str());
01792 
01793   // Show if device in database, and use preset vendor attribute
01794   // options unless user has requested otherwise.
01795   if (cfg.ignorepresets)
01796     PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name);
01797   else {
01798     // Apply vendor specific presets, print warning if present
01799     const drive_settings * dbentry = lookup_drive_apply_presets(
01800       &drive, cfg.attribute_defs, cfg.firmwarebugs);
01801     if (!dbentry)
01802       PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name);
01803     else {
01804       PrintOut(LOG_INFO, "Device: %s, found in smartd database%s%s\n",
01805         name, (*dbentry->modelfamily ? ": " : "."), (*dbentry->modelfamily ? dbentry->modelfamily : ""));
01806       if (*dbentry->warningmsg)
01807         PrintOut(LOG_CRIT, "Device: %s, WARNING: %s\n", name, dbentry->warningmsg);
01808     }
01809   }
01810 
01811   // Set default '-C 197[+]' if no '-C ID' is specified.
01812   if (!cfg.curr_pending_set)
01813     cfg.curr_pending_id = get_unc_attr_id(false, cfg.attribute_defs, cfg.curr_pending_incr);
01814   // Set default '-U 198[+]' if no '-U ID' is specified.
01815   if (!cfg.offl_pending_set)
01816     cfg.offl_pending_id = get_unc_attr_id(true, cfg.attribute_defs, cfg.offl_pending_incr);
01817 
01818   // If requested, show which presets would be used for this drive
01819   if (cfg.showpresets) {
01820     int savedebugmode=debugmode;
01821     PrintOut(LOG_INFO, "Device %s: presets are:\n", name);
01822     if (!debugmode)
01823       debugmode=2;
01824     show_presets(&drive);
01825     debugmode=savedebugmode;
01826   }
01827 
01828   // see if drive supports SMART
01829   supported=ataSmartSupport(&drive);
01830   if (supported!=1) {
01831     if (supported==0)
01832       // drive does NOT support SMART
01833       PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name);
01834     else
01835       // can't tell if drive supports SMART
01836       PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name);
01837   
01838     // should we proceed anyway?
01839     if (cfg.permissive) {
01840       PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name);
01841     }
01842     else {
01843       PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name);
01844       CloseDevice(atadev, name);
01845       return 2;
01846     }
01847   }
01848   
01849   if (ataEnableSmart(atadev)) {
01850     // Enable SMART command has failed
01851     PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
01852     CloseDevice(atadev, name);
01853     return 2; 
01854   }
01855   
01856   // disable device attribute autosave...
01857   if (cfg.autosave==1) {
01858     if (ataDisableAutoSave(atadev))
01859       PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name);
01860     else
01861       PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name);
01862   }
01863 
01864   // or enable device attribute autosave
01865   if (cfg.autosave==2) {
01866     if (ataEnableAutoSave(atadev))
01867       PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name);
01868     else
01869       PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name);
01870   }
01871 
01872   // capability check: SMART status
01873   if (cfg.smartcheck && ataSmartStatus2(atadev) == -1) {
01874     PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
01875     cfg.smartcheck = false;
01876   }
01877   
01878   // capability check: Read smart values and thresholds.  Note that
01879   // smart values are ALSO needed even if we ONLY want to know if the
01880   // device is self-test log or error-log capable!  After ATA-5, this
01881   // information was ALSO reproduced in the IDENTIFY DEVICE response,
01882   // but sadly not for ATA-5.  Sigh.
01883 
01884   // do we need to get SMART data?
01885   bool smart_val_ok = false;
01886   if (   cfg.autoofflinetest || cfg.selftest
01887       || cfg.errorlog        || cfg.xerrorlog
01888       || cfg.offlinests      || cfg.selfteststs
01889       || cfg.usagefailed     || cfg.prefail  || cfg.usage
01890       || cfg.tempdiff        || cfg.tempinfo || cfg.tempcrit
01891       || cfg.curr_pending_id || cfg.offl_pending_id         ) {
01892 
01893     if (ataReadSmartValues(atadev, &state.smartval)) {
01894       PrintOut(LOG_INFO, "Device: %s, Read SMART Values failed\n", name);
01895       cfg.usagefailed = cfg.prefail = cfg.usage = false;
01896       cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
01897       cfg.curr_pending_id = cfg.offl_pending_id = 0;
01898     }
01899     else {
01900       smart_val_ok = true;
01901       if (ataReadSmartThresholds(atadev, &state.smartthres)) {
01902         PrintOut(LOG_INFO, "Device: %s, Read SMART Thresholds failed%s\n",
01903                  name, (cfg.usagefailed ? ", ignoring -f Directive" : ""));
01904         cfg.usagefailed = false;
01905         // Let ata_get_attr_state() return ATTRSTATE_NO_THRESHOLD:
01906         memset(&state.smartthres, 0, sizeof(state.smartthres));
01907       }
01908     }
01909 
01910     // see if the necessary Attribute is there to monitor offline or
01911     // current pending sectors or temperature
01912     if (   cfg.curr_pending_id
01913         && !check_pending_id(cfg, state, cfg.curr_pending_id,
01914               "Current_Pending_Sector"))
01915       cfg.curr_pending_id = 0;
01916 
01917     if (   cfg.offl_pending_id
01918         && !check_pending_id(cfg, state, cfg.offl_pending_id,
01919               "Offline_Uncorrectable"))
01920       cfg.offl_pending_id = 0;
01921 
01922     if (   (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
01923         && !ata_return_temperature_value(&state.smartval, cfg.attribute_defs)) {
01924       PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n",
01925                name, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit);
01926       cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
01927     }
01928 
01929     // Report ignored '-r' or '-R' directives
01930     for (int id = 1; id <= 255; id++) {
01931       if (cfg.monitor_attr_flags.is_set(id, MONITOR_RAW_PRINT)) {
01932         char opt = (!cfg.monitor_attr_flags.is_set(id, MONITOR_RAW) ? 'r' : 'R');
01933         const char * excl = (cfg.monitor_attr_flags.is_set(id,
01934           (opt == 'r' ? MONITOR_AS_CRIT : MONITOR_RAW_AS_CRIT)) ? "!" : "");
01935 
01936         int idx = ata_find_attr_index(id, state.smartval);
01937         if (idx < 0)
01938           PrintOut(LOG_INFO,"Device: %s, no Attribute %d, ignoring -%c %d%s\n", name, id, opt, id, excl);
01939         else {
01940           bool prefail = !!ATTRIBUTE_FLAGS_PREFAILURE(state.smartval.vendor_attributes[idx].flags);
01941           if (!((prefail && cfg.prefail) || (!prefail && cfg.usage)))
01942             PrintOut(LOG_INFO,"Device: %s, not monitoring %s Attributes, ignoring -%c %d%s\n", name,
01943                      (prefail ? "Prefailure" : "Usage"), opt, id, excl);
01944         }
01945       }
01946     }
01947   }
01948   
01949   // enable/disable automatic on-line testing
01950   if (cfg.autoofflinetest) {
01951     // is this an enable or disable request?
01952     const char *what=(cfg.autoofflinetest==1)?"disable":"enable";
01953     if (!smart_val_ok)
01954       PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what);
01955     else {
01956       // if command appears unsupported, issue a warning...
01957       if (!isSupportAutomaticTimer(&state.smartval))
01958         PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name);
01959       // ... but then try anyway
01960       if ((cfg.autoofflinetest==1)?ataDisableAutoOffline(atadev):ataEnableAutoOffline(atadev))
01961         PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what);
01962       else
01963         PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what);
01964     }
01965   }
01966 
01967   // Read log directories if required for capability check
01968   ata_smart_log_directory smart_logdir, gp_logdir;
01969   bool smart_logdir_ok = false, gp_logdir_ok = false;
01970 
01971   if (   isGeneralPurposeLoggingCapable(&drive)
01972       && (cfg.errorlog || cfg.selftest)
01973       && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) {
01974       if (!ataReadLogDirectory(atadev, &smart_logdir, false))
01975         smart_logdir_ok = true;
01976   }
01977 
01978   if (cfg.xerrorlog && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) {
01979     if (!ataReadLogDirectory(atadev, &gp_logdir, true))
01980       gp_logdir_ok = true;
01981   }
01982 
01983   // capability check: self-test-log
01984   state.selflogcount = 0; state.selfloghour = 0;
01985   if (cfg.selftest) {
01986     int retval;
01987     if (!(   cfg.permissive
01988           || ( smart_logdir_ok && smart_logdir.entry[0x06-1].numsectors)
01989           || (!smart_logdir_ok && smart_val_ok && isSmartTestLogCapable(&state.smartval, &drive)))) {
01990       PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest (override with -T permissive)\n", name);
01991       cfg.selftest = false;
01992     }
01993     else if ((retval = SelfTestErrorCount(atadev, name, cfg.firmwarebugs)) < 0) {
01994       PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest\n", name);
01995       cfg.selftest = false;
01996     }
01997     else {
01998       state.selflogcount=SELFTEST_ERRORCOUNT(retval);
01999       state.selfloghour =SELFTEST_ERRORHOURS(retval);
02000     }
02001   }
02002   
02003   // capability check: ATA error log
02004   state.ataerrorcount = 0;
02005   if (cfg.errorlog) {
02006     int errcnt1;
02007     if (!(   cfg.permissive
02008           || ( smart_logdir_ok && smart_logdir.entry[0x01-1].numsectors)
02009           || (!smart_logdir_ok && smart_val_ok && isSmartErrorLogCapable(&state.smartval, &drive)))) {
02010       PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error (override with -T permissive)\n", name);
02011       cfg.errorlog = false;
02012     }
02013     else if ((errcnt1 = read_ata_error_count(atadev, name, cfg.firmwarebugs, false)) < 0) {
02014       PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error\n", name);
02015       cfg.errorlog = false;
02016     }
02017     else
02018       state.ataerrorcount = errcnt1;
02019   }
02020 
02021   if (cfg.xerrorlog) {
02022     int errcnt2;
02023     if (!(   cfg.permissive || cfg.firmwarebugs.is_set(BUG_NOLOGDIR)
02024           || (gp_logdir_ok && gp_logdir.entry[0x03-1].numsectors)   )) {
02025       PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror (override with -T permissive)\n",
02026                name);
02027       cfg.xerrorlog = false;
02028     }
02029     else if ((errcnt2 = read_ata_error_count(atadev, name, cfg.firmwarebugs, true)) < 0) {
02030       PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror\n", name);
02031       cfg.xerrorlog = false;
02032     }
02033     else if (cfg.errorlog && state.ataerrorcount != errcnt2) {
02034       PrintOut(LOG_INFO, "Device: %s, SMART Error Logs report different error counts: %d != %d\n",
02035                name, state.ataerrorcount, errcnt2);
02036       // Record max error count
02037       if (errcnt2 > state.ataerrorcount)
02038         state.ataerrorcount = errcnt2;
02039     }
02040     else
02041       state.ataerrorcount = errcnt2;
02042   }
02043 
02044   // capability check: self-test and offline data collection status
02045   if (cfg.offlinests || cfg.selfteststs) {
02046     if (!(cfg.permissive || (smart_val_ok && state.smartval.offline_data_collection_capability))) {
02047       if (cfg.offlinests)
02048         PrintOut(LOG_INFO, "Device: %s, no SMART Offline Data Collection capability, ignoring -l offlinests (override with -T permissive)\n", name);
02049       if (cfg.selfteststs)
02050         PrintOut(LOG_INFO, "Device: %s, no SMART Self-test capability, ignoring -l selfteststs (override with -T permissive)\n", name);
02051       cfg.offlinests = cfg.selfteststs = false;
02052     }
02053   }
02054 
02055   // capabilities check -- does it support powermode?
02056   if (cfg.powermode) {
02057     int powermode = ataCheckPowerMode(atadev);
02058     
02059     if (-1 == powermode) {
02060       PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
02061       cfg.powermode=0;
02062     } 
02063     else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) {
02064       PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
02065                name, powermode);
02066       cfg.powermode=0;
02067     }
02068   }
02069 
02070   // Apply ATA settings
02071   std::string msg;
02072 
02073   if (cfg.set_aam)
02074     format_set_result_msg(msg, "AAM", (cfg.set_aam > 0 ?
02075       ata_set_features(atadev, ATA_ENABLE_AAM, cfg.set_aam-1) :
02076       ata_set_features(atadev, ATA_DISABLE_AAM)), cfg.set_aam, true);
02077 
02078   if (cfg.set_apm)
02079     format_set_result_msg(msg, "APM", (cfg.set_apm > 0 ?
02080       ata_set_features(atadev, ATA_ENABLE_APM, cfg.set_apm-1) :
02081       ata_set_features(atadev, ATA_DISABLE_APM)), cfg.set_apm, true);
02082 
02083   if (cfg.set_lookahead)
02084     format_set_result_msg(msg, "Rd-ahead", ata_set_features(atadev,
02085       (cfg.set_lookahead > 0 ? ATA_ENABLE_READ_LOOK_AHEAD : ATA_DISABLE_READ_LOOK_AHEAD)),
02086       cfg.set_lookahead);
02087 
02088   if (cfg.set_wcache)
02089     format_set_result_msg(msg, "Wr-cache", ata_set_features(atadev,
02090       (cfg.set_wcache > 0? ATA_ENABLE_WRITE_CACHE : ATA_DISABLE_WRITE_CACHE)), cfg.set_wcache);
02091 
02092   if (cfg.set_security_freeze)
02093     format_set_result_msg(msg, "Security freeze",
02094       ata_nodata_command(atadev, ATA_SECURITY_FREEZE_LOCK));
02095 
02096   if (cfg.set_standby)
02097     format_set_result_msg(msg, "Standby",
02098       ata_nodata_command(atadev, ATA_IDLE, cfg.set_standby-1), cfg.set_standby, true);
02099 
02100   // Report as one log entry
02101   if (!msg.empty())
02102     PrintOut(LOG_INFO, "Device: %s, ATA settings applied: %s\n", name, msg.c_str());
02103 
02104   // set SCT Error Recovery Control if requested
02105   if (cfg.sct_erc_set) {
02106     if (!isSCTErrorRecoveryControlCapable(&drive))
02107       PrintOut(LOG_INFO, "Device: %s, no SCT Error Recovery Control support, ignoring -l scterc\n",
02108                name);
02109     else if (   ataSetSCTErrorRecoveryControltime(atadev, 1, cfg.sct_erc_readtime )
02110              || ataSetSCTErrorRecoveryControltime(atadev, 2, cfg.sct_erc_writetime))
02111       PrintOut(LOG_INFO, "Device: %s, set of SCT Error Recovery Control failed\n", name);
02112     else
02113       PrintOut(LOG_INFO, "Device: %s, SCT Error Recovery Control set to: Read: %u, Write: %u\n",
02114                name, cfg.sct_erc_readtime, cfg.sct_erc_writetime);
02115   }
02116 
02117   // If no tests available or selected, return
02118   if (!(   cfg.smartcheck  || cfg.selftest
02119         || cfg.errorlog    || cfg.xerrorlog
02120         || cfg.offlinests  || cfg.selfteststs
02121         || cfg.usagefailed || cfg.prefail  || cfg.usage
02122         || cfg.tempdiff    || cfg.tempinfo || cfg.tempcrit)) {
02123     CloseDevice(atadev, name);
02124     return 3;
02125   }
02126   
02127   // tell user we are registering device
02128   PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name);
02129   
02130   // close file descriptor
02131   CloseDevice(atadev, name);
02132 
02133   if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) {
02134     // Build file name for state file
02135     std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_');
02136     std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_');
02137     if (!state_path_prefix.empty()) {
02138       cfg.state_file = strprintf("%s%s-%s.ata.state", state_path_prefix.c_str(), model, serial);
02139       // Read previous state
02140       if (read_dev_state(cfg.state_file.c_str(), state)) {
02141         PrintOut(LOG_INFO, "Device: %s, state read from %s\n", name, cfg.state_file.c_str());
02142         // Copy ATA attribute values to temp state
02143         state.update_temp_state();
02144       }
02145     }
02146     if (!attrlog_path_prefix.empty())
02147       cfg.attrlog_file = strprintf("%s%s-%s.ata.csv", attrlog_path_prefix.c_str(), model, serial);
02148   }
02149 
02150   finish_device_scan(cfg, state);
02151 
02152   return 0;
02153 }
02154 
02155 // on success, return 0. On failure, return >0.  Never return <0,
02156 // please.
02157 static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scsidev)
02158 {
02159   int k, err, req_len, avail_len, version, len;
02160   const char *device = cfg.name.c_str();
02161   struct scsi_iec_mode_page iec;
02162   UINT8  tBuf[64];
02163   UINT8  inqBuf[96];
02164   UINT8  vpdBuf[252];
02165   char lu_id[64], serial[256], vendor[40], model[40];
02166 
02167   // Device must be open
02168   memset(inqBuf, 0, 96);
02169   req_len = 36;
02170   if ((err = scsiStdInquiry(scsidev, inqBuf, req_len))) {
02171     /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
02172     req_len = 64;
02173     if ((err = scsiStdInquiry(scsidev, inqBuf, req_len))) {
02174       PrintOut(LOG_INFO, "Device: %s, Both 36 and 64 byte INQUIRY failed; "
02175                "skip device\n", device);
02176       return 2;
02177     }
02178   }
02179   version = (inqBuf[2] & 0x7f); /* Accept old ISO/IEC 9316:1995 variants */
02180 
02181   avail_len = inqBuf[4] + 5;
02182   len = (avail_len < req_len) ? avail_len : req_len;
02183   if (len < 36) {
02184     PrintOut(LOG_INFO, "Device: %s, INQUIRY response less than 36 bytes; "
02185              "skip device\n", device);
02186     return 2;
02187   }
02188 
02189   int pdt = inqBuf[0] & 0x1f;
02190 
02191   if (! ((0 == pdt) || (4 == pdt) || (5 == pdt) || (7 == pdt) ||
02192          (0xe == pdt))) {
02193     PrintOut(LOG_INFO, "Device: %s, not a disk like device [PDT=0x%x], "
02194              "skip\n", device, pdt);
02195     return 2;
02196   }
02197 
02198   if (supported_vpd_pages_p) {
02199     delete supported_vpd_pages_p;
02200     supported_vpd_pages_p = NULL;
02201   }
02202   supported_vpd_pages_p = new supported_vpd_pages(scsidev);
02203 
02204   lu_id[0] = '\0';
02205   if ((version >= 0x3) && (version < 0x8)) {
02206     /* SPC to SPC-5 */
02207     if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_DEVICE_IDENTIFICATION,
02208                             vpdBuf, sizeof(vpdBuf))) {
02209       len = vpdBuf[3];
02210       scsi_decode_lu_dev_id(vpdBuf + 4, len, lu_id, sizeof(lu_id), NULL);
02211     }
02212   }
02213   serial[0] = '\0';
02214   if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_UNIT_SERIAL_NUMBER,
02215                           vpdBuf, sizeof(vpdBuf))) {
02216           len = vpdBuf[3];
02217           vpdBuf[4 + len] = '\0';
02218           scsi_format_id_string(serial, (const unsigned char *)&vpdBuf[4], len);
02219   }
02220 
02221   unsigned int lb_size;
02222   char si_str[64];
02223   uint64_t capacity = scsiGetSize(scsidev, &lb_size, NULL);
02224 
02225   if (capacity)
02226     format_capacity(si_str, sizeof(si_str), capacity);
02227   else
02228     si_str[0] = '\0';
02229 
02230   // Format device id string for warning emails
02231   cfg.dev_idinfo = strprintf("[%.8s %.16s %.4s]%s%s%s%s%s%s",
02232                      (char *)&inqBuf[8], (char *)&inqBuf[16], (char *)&inqBuf[32],
02233                      (lu_id[0] ? ", lu id: " : ""), (lu_id[0] ? lu_id : ""),
02234                      (serial[0] ? ", S/N: " : ""), (serial[0] ? serial : ""),
02235                      (si_str[0] ? ", " : ""), (si_str[0] ? si_str : ""));
02236   
02237   // format "model" string
02238   scsi_format_id_string(vendor, (const unsigned char *)&inqBuf[8], 8);
02239   scsi_format_id_string(model, (const unsigned char *)&inqBuf[16], 16);
02240   PrintOut(LOG_INFO, "Device: %s, %s\n", device, cfg.dev_idinfo.c_str());
02241 
02242   // check that device is ready for commands. IE stores its stuff on
02243   // the media.
02244   if ((err = scsiTestUnitReady(scsidev))) {
02245     if (SIMPLE_ERR_NOT_READY == err)
02246       PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device);
02247     else if (SIMPLE_ERR_NO_MEDIUM == err)
02248       PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device);
02249     else if (SIMPLE_ERR_BECOMING_READY == err)
02250       PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device);
02251     else
02252       PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err);
02253     CloseDevice(scsidev, device);
02254     return 2; 
02255   }
02256   
02257   // Badly-conforming USB storage devices may fail this check.
02258   // The response to the following IE mode page fetch (current and
02259   // changeable values) is carefully examined. It has been found
02260   // that various USB devices that malform the response will lock up
02261   // if asked for a log page (e.g. temperature) so it is best to
02262   // bail out now.
02263   if (!(err = scsiFetchIECmpage(scsidev, &iec, state.modese_len)))
02264     state.modese_len = iec.modese_len;
02265   else if (SIMPLE_ERR_BAD_FIELD == err)
02266     ;  /* continue since it is reasonable not to support IE mpage */
02267   else { /* any other error (including malformed response) unreasonable */
02268     PrintOut(LOG_INFO, 
02269              "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", 
02270              device, err);
02271     CloseDevice(scsidev, device);
02272     return 3;
02273   }
02274   
02275   // N.B. The following is passive (i.e. it doesn't attempt to turn on
02276   // smart if it is off). This may change to be the same as the ATA side.
02277   if (!scsi_IsExceptionControlEnabled(&iec)) {
02278     PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n"
02279                        "Try 'smartctl -s on %s' to turn on SMART features\n", 
02280                         device, device);
02281     CloseDevice(scsidev, device);
02282     return 3;
02283   }
02284   
02285   // Flag that certain log pages are supported (information may be
02286   // available from other sources).
02287   if (0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
02288     for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
02289       switch (tBuf[k]) { 
02290       case TEMPERATURE_LPAGE:
02291         state.TempPageSupported = 1;
02292         break;
02293       case IE_LPAGE:
02294         state.SmartPageSupported = 1;
02295         break;
02296       case READ_ERROR_COUNTER_LPAGE:
02297         state.ReadECounterPageSupported = 1;
02298         break;
02299       case WRITE_ERROR_COUNTER_LPAGE:
02300         state.WriteECounterPageSupported = 1;
02301         break;
02302       case VERIFY_ERROR_COUNTER_LPAGE:
02303         state.VerifyECounterPageSupported = 1;
02304         break;
02305       case NON_MEDIUM_ERROR_LPAGE:
02306         state.NonMediumErrorPageSupported = 1;
02307         break;
02308       default:
02309         break;
02310       }
02311     }   
02312   }
02313   
02314   // Check if scsiCheckIE() is going to work
02315   {
02316     UINT8 asc = 0;
02317     UINT8 ascq = 0;
02318     UINT8 currenttemp = 0;
02319     UINT8 triptemp = 0;
02320     
02321     if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported,
02322                     &asc, &ascq, &currenttemp, &triptemp)) {
02323       PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device);
02324       state.SuppressReport = 1;
02325       if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) {
02326         PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n",
02327                  device, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit);
02328         cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
02329       }
02330     }
02331   }
02332   
02333   // capability check: self-test-log
02334   if (cfg.selftest){
02335     int retval = scsiCountFailedSelfTests(scsidev, 0);
02336     if (retval<0) {
02337       // no self-test log, turn off monitoring
02338       PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device);
02339       cfg.selftest = false;
02340       state.selflogcount = 0;
02341       state.selfloghour = 0;
02342     }
02343     else {
02344       // register starting values to watch for changes
02345       state.selflogcount=SELFTEST_ERRORCOUNT(retval);
02346       state.selfloghour =SELFTEST_ERRORHOURS(retval);
02347     }
02348   }
02349   
02350   // disable autosave (set GLTSD bit)
02351   if (cfg.autosave==1){
02352     if (scsiSetControlGLTSD(scsidev, 1, state.modese_len))
02353       PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device);
02354     else
02355       PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device);
02356   }
02357 
02358   // or enable autosave (clear GLTSD bit)
02359   if (cfg.autosave==2){
02360     if (scsiSetControlGLTSD(scsidev, 0, state.modese_len))
02361       PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device);
02362     else
02363       PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device);
02364   }
02365   
02366   // tell user we are registering device
02367   PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device);
02368 
02369   // Make sure that init_standby_check() ignores SCSI devices
02370   cfg.offlinests_ns = cfg.selfteststs_ns = false;
02371 
02372   // close file descriptor
02373   CloseDevice(scsidev, device);
02374 
02375   if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) {
02376     // Build file name for state file
02377     std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_');
02378     std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_');
02379     if (!state_path_prefix.empty()) {
02380       cfg.state_file = strprintf("%s%s-%s-%s.scsi.state", state_path_prefix.c_str(), vendor, model, serial);
02381       // Read previous state
02382       if (read_dev_state(cfg.state_file.c_str(), state)) {
02383         PrintOut(LOG_INFO, "Device: %s, state read from %s\n", device, cfg.state_file.c_str());
02384         // Copy ATA attribute values to temp state
02385         state.update_temp_state();
02386       }
02387     }
02388     if (!attrlog_path_prefix.empty())
02389       cfg.attrlog_file = strprintf("%s%s-%s-%s.scsi.csv", attrlog_path_prefix.c_str(), vendor, model, serial);
02390   }
02391 
02392   finish_device_scan(cfg, state);
02393 
02394   return 0;
02395 }
02396 
02397 // If the self-test log has got more self-test errors (or more recent
02398 // self-test errors) recorded, then notify user.
02399 static void CheckSelfTestLogs(const dev_config & cfg, dev_state & state, int newi)
02400 {
02401   const char * name = cfg.name.c_str();
02402 
02403   if (newi<0)
02404     // command failed
02405     MailWarning(cfg, state, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
02406   else {
02407     reset_warning_mail(cfg, state, 8, "Read SMART Self-Test Log worked again");
02408 
02409     // old and new error counts
02410     int oldc=state.selflogcount;
02411     int newc=SELFTEST_ERRORCOUNT(newi);
02412     
02413     // old and new error timestamps in hours
02414     int oldh=state.selfloghour;
02415     int newh=SELFTEST_ERRORHOURS(newi);
02416     
02417     if (oldc<newc) {
02418       // increase in error count
02419       PrintOut(LOG_CRIT, "Device: %s, Self-Test Log error count increased from %d to %d\n",
02420                name, oldc, newc);
02421       MailWarning(cfg, state, 3, "Device: %s, Self-Test Log error count increased from %d to %d",
02422                    name, oldc, newc);
02423       state.must_write = true;
02424     }
02425     else if (newc > 0 && oldh != newh) {
02426       // more recent error
02427       // a 'more recent' error might actually be a smaller hour number,
02428       // if the hour number has wrapped.
02429       // There's still a bug here.  You might just happen to run a new test
02430       // exactly 32768 hours after the previous failure, and have run exactly
02431       // 20 tests between the two, in which case smartd will miss the
02432       // new failure.
02433       PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
02434                name, newh);
02435       MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d",
02436                    name, newh);
02437       state.must_write = true;
02438     }
02439 
02440     // Print info if error entries have disappeared
02441     // or newer successful successful extended self-test exits
02442     if (oldc > newc) {
02443       PrintOut(LOG_INFO, "Device: %s, Self-Test Log error count decreased from %d to %d\n",
02444                name, oldc, newc);
02445       if (newc == 0)
02446         reset_warning_mail(cfg, state, 3, "Self-Test Log does no longer report errors");
02447     }
02448 
02449     // Needed since self-test error count may DECREASE.  Hour might
02450     // also have changed.
02451     state.selflogcount= newc;
02452     state.selfloghour = newh;
02453   }
02454   return;
02455 }
02456 
02457 // Test types, ordered by priority.
02458 static const char test_type_chars[] = "LncrSCO";
02459 static const unsigned num_test_types = sizeof(test_type_chars)-1;
02460 
02461 // returns test type if time to do test of type testtype,
02462 // 0 if not time to do test.
02463 static char next_scheduled_test(const dev_config & cfg, dev_state & state, bool scsi, time_t usetime = 0)
02464 {
02465   // check that self-testing has been requested
02466   if (cfg.test_regex.empty())
02467     return 0;
02468 
02469   // Exit if drive not capable of any test
02470   if ( state.not_cap_long && state.not_cap_short &&
02471       (scsi || (state.not_cap_conveyance && state.not_cap_offline)))
02472     return 0;
02473 
02474   // since we are about to call localtime(), be sure glibc is informed
02475   // of any timezone changes we make.
02476   if (!usetime)
02477     FixGlibcTimeZoneBug();
02478   
02479   // Is it time for next check?
02480   time_t now = (!usetime ? time(0) : usetime);
02481   if (now < state.scheduled_test_next_check)
02482     return 0;
02483 
02484   // Limit time check interval to 90 days
02485   if (state.scheduled_test_next_check + (3600L*24*90) < now)
02486     state.scheduled_test_next_check = now - (3600L*24*90);
02487 
02488   // Check interval [state.scheduled_test_next_check, now] for scheduled tests
02489   char testtype = 0;
02490   time_t testtime = 0; int testhour = 0;
02491   int maxtest = num_test_types-1;
02492 
02493   for (time_t t = state.scheduled_test_next_check; ; ) {
02494     struct tm * tms = localtime(&t);
02495     // tm_wday is 0 (Sunday) to 6 (Saturday).  We use 1 (Monday) to 7 (Sunday).
02496     int weekday = (tms->tm_wday ? tms->tm_wday : 7);
02497     for (int i = 0; i <= maxtest; i++) {
02498       // Skip if drive not capable of this test
02499       switch (test_type_chars[i]) {
02500         case 'L': if (state.not_cap_long)       continue; break;
02501         case 'S': if (state.not_cap_short)      continue; break;
02502         case 'C': if (scsi || state.not_cap_conveyance) continue; break;
02503         case 'O': if (scsi || state.not_cap_offline)    continue; break;
02504         case 'c': case 'n':
02505         case 'r': if (scsi || state.not_cap_selective)  continue; break;
02506         default: continue;
02507       }
02508       // Try match of "T/MM/DD/d/HH"
02509       char pattern[16];
02510       snprintf(pattern, sizeof(pattern), "%c/%02d/%02d/%1d/%02d",
02511         test_type_chars[i], tms->tm_mon+1, tms->tm_mday, weekday, tms->tm_hour);
02512       if (cfg.test_regex.full_match(pattern)) {
02513         // Test found
02514         testtype = pattern[0];
02515         testtime = t; testhour = tms->tm_hour;
02516         // Limit further matches to higher priority self-tests
02517         maxtest = i-1;
02518         break;
02519       }
02520     }
02521     // Exit if no tests left or current time reached
02522     if (maxtest < 0)
02523       break;
02524     if (t >= now)
02525       break;
02526     // Check next hour
02527     if ((t += 3600) > now)
02528       t = now;
02529   }
02530   
02531   // Do next check not before next hour.
02532   struct tm * tmnow = localtime(&now);
02533   state.scheduled_test_next_check = now + (3600 - tmnow->tm_min*60 - tmnow->tm_sec);
02534 
02535   if (testtype) {
02536     state.must_write = true;
02537     // Tell user if an old test was found.
02538     if (!usetime && !(testhour == tmnow->tm_hour && testtime + 3600 > now)) {
02539       char datebuf[DATEANDEPOCHLEN]; dateandtimezoneepoch(datebuf, testtime);
02540       PrintOut(LOG_INFO, "Device: %s, old test of type %c not run at %s, starting now.\n",
02541         cfg.name.c_str(), testtype, datebuf);
02542     }
02543   }
02544 
02545   return testtype;
02546 }
02547 
02548 // Print a list of future tests.
02549 static void PrintTestSchedule(const dev_config_vector & configs, dev_state_vector & states, const smart_device_list & devices)
02550 {
02551   unsigned numdev = configs.size();
02552   if (!numdev)
02553     return;
02554   std::vector<int> testcnts(numdev * num_test_types, 0);
02555 
02556   PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n");
02557 
02558   // FixGlibcTimeZoneBug(); // done in PrintOut()
02559   time_t now = time(0);
02560   char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN];
02561   dateandtimezoneepoch(datenow, now);
02562 
02563   long seconds;
02564   for (seconds=checktime; seconds<3600L*24*90; seconds+=checktime) {
02565     // Check for each device whether a test will be run
02566     time_t testtime = now + seconds;
02567     for (unsigned i = 0; i < numdev; i++) {
02568       const dev_config & cfg = configs.at(i);
02569       dev_state & state = states.at(i);
02570       const char * p;
02571       char testtype = next_scheduled_test(cfg, state, devices.at(i)->is_scsi(), testtime);
02572       if (testtype && (p = strchr(test_type_chars, testtype))) {
02573         unsigned t = (p - test_type_chars);
02574         // Report at most 5 tests of each type
02575         if (++testcnts[i*num_test_types + t] <= 5) {
02576           dateandtimezoneepoch(date, testtime);
02577           PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg.name.c_str(),
02578             testcnts[i*num_test_types + t], testtype, date);
02579         }
02580       }
02581     }
02582   }
02583 
02584   // Report totals
02585   dateandtimezoneepoch(date, now+seconds);
02586   PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
02587   for (unsigned i = 0; i < numdev; i++) {
02588     const dev_config & cfg = configs.at(i);
02589     bool scsi = devices.at(i)->is_scsi();
02590     for (unsigned t = 0; t < num_test_types; t++) {
02591       int cnt = testcnts[i*num_test_types + t];
02592       if (cnt == 0 && !strchr((scsi ? "LS" : "LSCO"), test_type_chars[t]))
02593         continue;
02594       PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg.name.c_str(),
02595         cnt, (cnt==1?"":"s"), test_type_chars[t]);
02596     }
02597   }
02598 
02599 }
02600 
02601 // Return zero on success, nonzero on failure. Perform offline (background)
02602 // short or long (extended) self test on given scsi device.
02603 static int DoSCSISelfTest(const dev_config & cfg, dev_state & state, scsi_device * device, char testtype)
02604 {
02605   int retval = 0;
02606   const char *testname = 0;
02607   const char *name = cfg.name.c_str();
02608   int inProgress;
02609 
02610   if (scsiSelfTestInProgress(device, &inProgress)) {
02611     PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
02612     state.not_cap_short = state.not_cap_long = true;
02613     return 1;
02614   }
02615 
02616   if (1 == inProgress) {
02617     PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in "
02618              "progress.\n", name);
02619     return 1;
02620   }
02621 
02622   switch (testtype) {
02623   case 'S':
02624     testname = "Short Self";
02625     retval = scsiSmartShortSelfTest(device);
02626     break;
02627   case 'L':
02628     testname = "Long Self";
02629     retval = scsiSmartExtendSelfTest(device);
02630     break;
02631   }
02632   // If we can't do the test, exit
02633   if (NULL == testname) {
02634     PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name, 
02635              testtype);
02636     return 1;
02637   }
02638   if (retval) {
02639     if ((SIMPLE_ERR_BAD_OPCODE == retval) || 
02640         (SIMPLE_ERR_BAD_FIELD == retval)) {
02641       PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, 
02642                testname);
02643       if ('L'==testtype)
02644         state.not_cap_long = true;
02645       else
02646         state.not_cap_short = true;
02647      
02648       return 1;
02649     }
02650     PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name, 
02651              testname, retval);
02652     return 1;
02653   }
02654   
02655   PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname);
02656   
02657   return 0;
02658 }
02659 
02660 // Do an offline immediate or self-test.  Return zero on success,
02661 // nonzero on failure.
02662 static int DoATASelfTest(const dev_config & cfg, dev_state & state, ata_device * device, char testtype)
02663 {
02664   const char *name = cfg.name.c_str();
02665 
02666   // Read current smart data and check status/capability
02667   struct ata_smart_values data;
02668   if (ataReadSmartValues(device, &data) || !(data.offline_data_collection_capability)) {
02669     PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name);
02670     return 1;
02671   }
02672   
02673   // Check for capability to do the test
02674   int dotest = -1, mode = 0;
02675   const char *testname = 0;
02676   switch (testtype) {
02677   case 'O':
02678     testname="Offline Immediate ";
02679     if (isSupportExecuteOfflineImmediate(&data))
02680       dotest=OFFLINE_FULL_SCAN;
02681     else
02682       state.not_cap_offline = true;
02683     break;
02684   case 'C':
02685     testname="Conveyance Self-";
02686     if (isSupportConveyanceSelfTest(&data))
02687       dotest=CONVEYANCE_SELF_TEST;
02688     else
02689       state.not_cap_conveyance = true;
02690     break;
02691   case 'S':
02692     testname="Short Self-";
02693     if (isSupportSelfTest(&data))
02694       dotest=SHORT_SELF_TEST;
02695     else
02696       state.not_cap_short = true;
02697     break;
02698   case 'L':
02699     testname="Long Self-";
02700     if (isSupportSelfTest(&data))
02701       dotest=EXTEND_SELF_TEST;
02702     else
02703       state.not_cap_long = true;
02704     break;
02705 
02706   case 'c': case 'n': case 'r':
02707     testname = "Selective Self-";
02708     if (isSupportSelectiveSelfTest(&data)) {
02709       dotest = SELECTIVE_SELF_TEST;
02710       switch (testtype) {
02711         case 'c': mode = SEL_CONT; break;
02712         case 'n': mode = SEL_NEXT; break;
02713         case 'r': mode = SEL_REDO; break;
02714       }
02715     }
02716     else
02717       state.not_cap_selective = true;
02718     break;
02719   }
02720   
02721   // If we can't do the test, exit
02722   if (dotest<0) {
02723     PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname);
02724     return 1;
02725   }
02726   
02727   // If currently running a self-test, do not interrupt it to start another.
02728   if (15==(data.self_test_exec_status >> 4)) {
02729     if (cfg.firmwarebugs.is_set(BUG_SAMSUNG3) && data.self_test_exec_status == 0xf0) {
02730       PrintOut(LOG_INFO, "Device: %s, will not skip scheduled %sTest "
02731                "despite unclear Self-Test byte (SAMSUNG Firmware bug).\n", name, testname);
02732     } else {
02733       PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n",
02734                name, testname, (int)(data.self_test_exec_status & 0x0f));
02735       return 1;
02736     }
02737   }
02738 
02739   if (dotest == SELECTIVE_SELF_TEST) {
02740     // Set test span
02741     ata_selective_selftest_args selargs, prev_args;
02742     selargs.num_spans = 1;
02743     selargs.span[0].mode = mode;
02744     prev_args.num_spans = 1;
02745     prev_args.span[0].start = state.selective_test_last_start;
02746     prev_args.span[0].end   = state.selective_test_last_end;
02747     if (ataWriteSelectiveSelfTestLog(device, selargs, &data, state.num_sectors, &prev_args)) {
02748       PrintOut(LOG_CRIT, "Device: %s, prepare %sTest failed\n", name, testname);
02749       return 1;
02750     }
02751     uint64_t start = selargs.span[0].start, end = selargs.span[0].end;
02752     PrintOut(LOG_INFO, "Device: %s, %s test span at LBA %" PRIu64 " - %" PRIu64 " (%" PRIu64 " sectors, %u%% - %u%% of disk).\n",
02753       name, (selargs.span[0].mode == SEL_NEXT ? "next" : "redo"),
02754       start, end, end - start + 1,
02755       (unsigned)((100 * start + state.num_sectors/2) / state.num_sectors),
02756       (unsigned)((100 * end   + state.num_sectors/2) / state.num_sectors));
02757     state.selective_test_last_start = start;
02758     state.selective_test_last_end = end;
02759   }
02760 
02761   // execute the test, and return status
02762   int retval = smartcommandhandler(device, IMMEDIATE_OFFLINE, dotest, NULL);
02763   if (retval) {
02764     PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
02765     return retval;
02766   }
02767 
02768   // Report recent test start to do_disable_standby_check()
02769   // and force log of next test status
02770   if (testtype == 'O')
02771     state.offline_started = true;
02772   else
02773     state.selftest_started = true;
02774 
02775   PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
02776   return 0;
02777 }
02778 
02779 // Check pending sector count attribute values (-C, -U directives).
02780 static void check_pending(const dev_config & cfg, dev_state & state,
02781                           unsigned char id, bool increase_only,
02782                           const ata_smart_values & smartval,
02783                           int mailtype, const char * msg)
02784 {
02785   // Find attribute index
02786   int i = ata_find_attr_index(id, smartval);
02787   if (!(i >= 0 && ata_find_attr_index(id, state.smartval) == i))
02788     return;
02789 
02790   // No report if no sectors pending.
02791   uint64_t rawval = ata_get_attr_raw_value(smartval.vendor_attributes[i], cfg.attribute_defs);
02792   if (rawval == 0) {
02793     reset_warning_mail(cfg, state, mailtype, "No more %s", msg);
02794     return;
02795   }
02796 
02797   // If attribute is not reset, report only sector count increases.
02798   uint64_t prev_rawval = ata_get_attr_raw_value(state.smartval.vendor_attributes[i], cfg.attribute_defs);
02799   if (!(!increase_only || prev_rawval < rawval))
02800     return;
02801 
02802   // Format message.
02803   std::string s = strprintf("Device: %s, %" PRId64 " %s", cfg.name.c_str(), rawval, msg);
02804   if (prev_rawval > 0 && rawval != prev_rawval)
02805     s += strprintf(" (changed %+" PRId64 ")", rawval - prev_rawval);
02806 
02807   PrintOut(LOG_CRIT, "%s\n", s.c_str());
02808   MailWarning(cfg, state, mailtype, "%s", s.c_str());
02809   state.must_write = true;
02810 }
02811 
02812 // Format Temperature value
02813 static const char * fmt_temp(unsigned char x, char (& buf)[20])
02814 {
02815   if (!x) // unset
02816     return "??";
02817   snprintf(buf, sizeof(buf), "%u", x);
02818   return buf;
02819 }
02820 
02821 // Check Temperature limits
02822 static void CheckTemperature(const dev_config & cfg, dev_state & state, unsigned char currtemp, unsigned char triptemp)
02823 {
02824   if (!(0 < currtemp && currtemp < 255)) {
02825     PrintOut(LOG_INFO, "Device: %s, failed to read Temperature\n", cfg.name.c_str());
02826     return;
02827   }
02828 
02829   // Update Max Temperature
02830   const char * minchg = "", * maxchg = "";
02831   if (currtemp > state.tempmax) {
02832     if (state.tempmax)
02833       maxchg = "!";
02834     state.tempmax = currtemp;
02835     state.must_write = true;
02836   }
02837 
02838   char buf[20];
02839   if (!state.temperature) {
02840     // First check
02841     if (!state.tempmin || currtemp < state.tempmin)
02842         // Delay Min Temperature update by ~ 30 minutes.
02843         state.tempmin_delay = time(0) + CHECKTIME - 60;
02844     PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d Celsius (Min/Max %s/%u%s)\n",
02845       cfg.name.c_str(), (int)currtemp, fmt_temp(state.tempmin, buf), state.tempmax, maxchg);
02846     if (triptemp)
02847       PrintOut(LOG_INFO, "    [trip Temperature is %d Celsius]\n", (int)triptemp);
02848     state.temperature = currtemp;
02849   }
02850   else {
02851     if (state.tempmin_delay) {
02852       // End Min Temperature update delay if ...
02853       if (   (state.tempmin && currtemp > state.tempmin) // current temp exceeds recorded min,
02854           || (state.tempmin_delay <= time(0))) {         // or delay time is over.
02855         state.tempmin_delay = 0;
02856         if (!state.tempmin)
02857           state.tempmin = 255;
02858       }
02859     }
02860 
02861     // Update Min Temperature
02862     if (!state.tempmin_delay && currtemp < state.tempmin) {
02863       state.tempmin = currtemp;
02864       state.must_write = true;
02865       if (currtemp != state.temperature)
02866         minchg = "!";
02867     }
02868 
02869     // Track changes
02870     if (cfg.tempdiff && (*minchg || *maxchg || abs((int)currtemp - (int)state.temperature) >= cfg.tempdiff)) {
02871       PrintOut(LOG_INFO, "Device: %s, Temperature changed %+d Celsius to %u Celsius (Min/Max %s%s/%u%s)\n",
02872         cfg.name.c_str(), (int)currtemp-(int)state.temperature, currtemp, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
02873       state.temperature = currtemp;
02874     }
02875   }
02876 
02877   // Check limits
02878   if (cfg.tempcrit && currtemp >= cfg.tempcrit) {
02879     PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n",
02880       cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
02881     MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)",
02882       cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
02883   }
02884   else if (cfg.tempinfo && currtemp >= cfg.tempinfo) {
02885     PrintOut(LOG_INFO, "Device: %s, Temperature %u Celsius reached limit of %u Celsius (Min/Max %s%s/%u%s)\n",
02886       cfg.name.c_str(), currtemp, cfg.tempinfo, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
02887   }
02888   else if (cfg.tempcrit) {
02889     unsigned char limit = (cfg.tempinfo ? cfg.tempinfo : cfg.tempcrit-5);
02890     if (currtemp < limit)
02891       reset_warning_mail(cfg, state, 12, "Temperature %u Celsius dropped below %u Celsius", currtemp, limit);
02892   }
02893 }
02894 
02895 // Check normalized and raw attribute values.
02896 static void check_attribute(const dev_config & cfg, dev_state & state,
02897                             const ata_smart_attribute & attr,
02898                             const ata_smart_attribute & prev,
02899                             int attridx,
02900                             const ata_smart_threshold_entry * thresholds)
02901 {
02902   // Check attribute and threshold
02903   ata_attr_state attrstate = ata_get_attr_state(attr, attridx, thresholds, cfg.attribute_defs);
02904   if (attrstate == ATTRSTATE_NON_EXISTING)
02905     return;
02906 
02907   // If requested, check for usage attributes that have failed.
02908   if (   cfg.usagefailed && attrstate == ATTRSTATE_FAILED_NOW
02909       && !cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGN_FAILUSE)) {
02910     std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm);
02911     PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", cfg.name.c_str(), attr.id, attrname.c_str());
02912     MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", cfg.name.c_str(), attr.id, attrname.c_str());
02913     state.must_write = true;
02914   }
02915 
02916   // Return if we're not tracking this type of attribute
02917   bool prefail = !!ATTRIBUTE_FLAGS_PREFAILURE(attr.flags);
02918   if (!(   ( prefail && cfg.prefail)
02919         || (!prefail && cfg.usage  )))
02920     return;
02921 
02922   // Return if '-I ID' was specified
02923   if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGNORE))
02924     return;
02925 
02926   // Issue warning if they don't have the same ID in all structures.
02927   if (attr.id != prev.id) {
02928     PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d\n",
02929              cfg.name.c_str(), attr.id, prev.id);
02930     return;
02931   }
02932 
02933   // Compare normalized values if valid.
02934   bool valchanged = false;
02935   if (attrstate > ATTRSTATE_NO_NORMVAL) {
02936     if (attr.current != prev.current)
02937       valchanged = true;
02938   }
02939 
02940   // Compare raw values if requested.
02941   bool rawchanged = false;
02942   if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW)) {
02943     if (   ata_get_attr_raw_value(attr, cfg.attribute_defs)
02944         != ata_get_attr_raw_value(prev, cfg.attribute_defs))
02945       rawchanged = true;
02946   }
02947 
02948   // Return if no change
02949   if (!(valchanged || rawchanged))
02950     return;
02951 
02952   // Format value strings
02953   std::string currstr, prevstr;
02954   if (attrstate == ATTRSTATE_NO_NORMVAL) {
02955     // Print raw values only
02956     currstr = strprintf("%s (Raw)",
02957       ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str());
02958     prevstr = strprintf("%s (Raw)",
02959       ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str());
02960   }
02961   else if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_PRINT)) {
02962     // Print normalized and raw values
02963     currstr = strprintf("%d [Raw %s]", attr.current,
02964       ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str());
02965     prevstr = strprintf("%d [Raw %s]", prev.current,
02966       ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str());
02967   }
02968   else {
02969     // Print normalized values only
02970     currstr = strprintf("%d", attr.current);
02971     prevstr = strprintf("%d", prev.current);
02972   }
02973 
02974   // Format message
02975   std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %s to %s",
02976                               cfg.name.c_str(), (prefail ? "Prefailure" : "Usage"), attr.id,
02977                               ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm).c_str(),
02978                               prevstr.c_str(), currstr.c_str());
02979 
02980   // Report this change as critical ?
02981   if (   (valchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_AS_CRIT))
02982       || (rawchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_AS_CRIT))) {
02983     PrintOut(LOG_CRIT, "%s\n", msg.c_str());
02984     MailWarning(cfg, state, 2, "%s", msg.c_str());
02985   }
02986   else {
02987     PrintOut(LOG_INFO, "%s\n", msg.c_str());
02988   }
02989   state.must_write = true;
02990 }
02991 
02992 
02993 static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device * atadev,
02994                           bool firstpass, bool allow_selftests)
02995 {
02996   const char * name = cfg.name.c_str();
02997 
02998   // If user has asked, test the email warning system
02999   if (cfg.emailtest)
03000     MailWarning(cfg, state, 0, "TEST EMAIL from smartd for device: %s", name);
03001 
03002   // if we can't open device, fail gracefully rather than hard --
03003   // perhaps the next time around we'll be able to open it.  ATAPI
03004   // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
03005   // given (see linux cdrom driver).
03006   if (!atadev->open()) {
03007     PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, atadev->get_errmsg());
03008     MailWarning(cfg, state, 9, "Device: %s, unable to open device", name);
03009     return 1;
03010   }
03011   if (debugmode)
03012     PrintOut(LOG_INFO,"Device: %s, opened ATA device\n", name);
03013   reset_warning_mail(cfg, state, 9, "open device worked again");
03014 
03015   // user may have requested (with the -n Directive) to leave the disk
03016   // alone if it is in idle or sleeping mode.  In this case check the
03017   // power mode and exit without check if needed
03018   if (cfg.powermode && !state.powermodefail) {
03019     int dontcheck=0, powermode=ataCheckPowerMode(atadev);
03020     const char * mode = 0;
03021     if (0 <= powermode && powermode < 0xff) {
03022       // wait for possible spin up and check again
03023       int powermode2;
03024       sleep(5);
03025       powermode2 = ataCheckPowerMode(atadev);
03026       if (powermode2 > powermode)
03027         PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
03028       powermode = powermode2;
03029     }
03030         
03031     switch (powermode){
03032     case -1:
03033       // SLEEP
03034       mode="SLEEP";
03035       if (cfg.powermode>=1)
03036         dontcheck=1;
03037       break;
03038     case 0:
03039       // STANDBY
03040       mode="STANDBY";
03041       if (cfg.powermode>=2)
03042         dontcheck=1;
03043       break;
03044     case 0x80:
03045       // IDLE
03046       mode="IDLE";
03047       if (cfg.powermode>=3)
03048         dontcheck=1;
03049       break;
03050     case 0xff:
03051       // ACTIVE/IDLE
03052       mode="ACTIVE or IDLE";
03053       break;
03054     default:
03055       // UNKNOWN
03056       PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
03057         name, powermode);
03058       state.powermodefail = true;
03059       break;
03060     }
03061 
03062     // if we are going to skip a check, return now
03063     if (dontcheck){
03064       // skip at most powerskipmax checks
03065       if (!cfg.powerskipmax || state.powerskipcnt<cfg.powerskipmax) {
03066         CloseDevice(atadev, name);
03067         if (!state.powerskipcnt && !cfg.powerquiet) // report first only and avoid waking up system disk
03068           PrintOut(LOG_INFO, "Device: %s, is in %s mode, suspending checks\n", name, mode);
03069         state.powerskipcnt++;
03070         return 0;
03071       }
03072       else {
03073         PrintOut(LOG_INFO, "Device: %s, %s mode ignored due to reached limit of skipped checks (%d check%s skipped)\n",
03074           name, mode, state.powerskipcnt, (state.powerskipcnt==1?"":"s"));
03075       }
03076       state.powerskipcnt = 0;
03077       state.tempmin_delay = time(0) + CHECKTIME - 60; // Delay Min Temperature update
03078     }
03079     else if (state.powerskipcnt) {
03080       PrintOut(LOG_INFO, "Device: %s, is back in %s mode, resuming checks (%d check%s skipped)\n",
03081         name, mode, state.powerskipcnt, (state.powerskipcnt==1?"":"s"));
03082       state.powerskipcnt = 0;
03083       state.tempmin_delay = time(0) + CHECKTIME - 60; // Delay Min Temperature update
03084     }
03085   }
03086 
03087   // check smart status
03088   if (cfg.smartcheck) {
03089     int status=ataSmartStatus2(atadev);
03090     if (status==-1){
03091       PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
03092       MailWarning(cfg, state, 5, "Device: %s, not capable of SMART self-check", name);
03093       state.must_write = true;
03094     }
03095     else if (status==1){
03096       PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name);
03097       MailWarning(cfg, state, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name);
03098       state.must_write = true;
03099     }
03100   }
03101   
03102   // Check everything that depends upon SMART Data (eg, Attribute values)
03103   if (   cfg.usagefailed || cfg.prefail || cfg.usage
03104       || cfg.curr_pending_id || cfg.offl_pending_id
03105       || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit
03106       || cfg.selftest ||  cfg.offlinests || cfg.selfteststs) {
03107 
03108     // Read current attribute values.
03109     ata_smart_values curval;
03110     if (ataReadSmartValues(atadev, &curval)){
03111       PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name);
03112       MailWarning(cfg, state, 6, "Device: %s, failed to read SMART Attribute Data", name);
03113       state.must_write = true;
03114     }
03115     else {
03116       reset_warning_mail(cfg, state, 6, "read SMART Attribute Data worked again");
03117 
03118       // look for current or offline pending sectors
03119       if (cfg.curr_pending_id)
03120         check_pending(cfg, state, cfg.curr_pending_id, cfg.curr_pending_incr, curval, 10,
03121                       (!cfg.curr_pending_incr ? "Currently unreadable (pending) sectors"
03122                                               : "Total unreadable (pending) sectors"    ));
03123 
03124       if (cfg.offl_pending_id)
03125         check_pending(cfg, state, cfg.offl_pending_id, cfg.offl_pending_incr, curval, 11,
03126                       (!cfg.offl_pending_incr ? "Offline uncorrectable sectors"
03127                                               : "Total offline uncorrectable sectors"));
03128 
03129       // check temperature limits
03130       if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
03131         CheckTemperature(cfg, state, ata_return_temperature_value(&curval, cfg.attribute_defs), 0);
03132 
03133       // look for failed usage attributes, or track usage or prefail attributes
03134       if (cfg.usagefailed || cfg.prefail || cfg.usage) {
03135         for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
03136           check_attribute(cfg, state,
03137                           curval.vendor_attributes[i],
03138                           state.smartval.vendor_attributes[i],
03139                           i, state.smartthres.thres_entries);
03140         }
03141       }
03142 
03143       // Log changes of offline data collection status
03144       if (cfg.offlinests) {
03145         if (   curval.offline_data_collection_status
03146                 != state.smartval.offline_data_collection_status
03147             || state.offline_started // test was started in previous call
03148             || (firstpass && (debugmode || (curval.offline_data_collection_status & 0x7d))))
03149           log_offline_data_coll_status(name, curval.offline_data_collection_status);
03150       }
03151 
03152       // Log changes of self-test execution status
03153       if (cfg.selfteststs) {
03154         if (   curval.self_test_exec_status != state.smartval.self_test_exec_status
03155             || state.selftest_started // test was started in previous call
03156             || (firstpass && (debugmode || curval.self_test_exec_status != 0x00)))
03157           log_self_test_exec_status(name, curval.self_test_exec_status);
03158       }
03159 
03160       // Save the new values for the next time around
03161       state.smartval = curval;
03162     }
03163   }
03164   state.offline_started = state.selftest_started = false;
03165   
03166   // check if number of selftest errors has increased (note: may also DECREASE)
03167   if (cfg.selftest)
03168     CheckSelfTestLogs(cfg, state, SelfTestErrorCount(atadev, name, cfg.firmwarebugs));
03169 
03170   // check if number of ATA errors has increased
03171   if (cfg.errorlog || cfg.xerrorlog) {
03172 
03173     int errcnt1 = -1, errcnt2 = -1;
03174     if (cfg.errorlog)
03175       errcnt1 = read_ata_error_count(atadev, name, cfg.firmwarebugs, false);
03176     if (cfg.xerrorlog)
03177       errcnt2 = read_ata_error_count(atadev, name, cfg.firmwarebugs, true);
03178 
03179     // new number of errors is max of both logs
03180     int newc = (errcnt1 >= errcnt2 ? errcnt1 : errcnt2);
03181 
03182     // did command fail?
03183     if (newc<0)
03184       // lack of PrintOut here is INTENTIONAL
03185       MailWarning(cfg, state, 7, "Device: %s, Read SMART Error Log Failed", name);
03186 
03187     // has error count increased?
03188     int oldc = state.ataerrorcount;
03189     if (newc>oldc){
03190       PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n",
03191                name, oldc, newc);
03192       MailWarning(cfg, state, 4, "Device: %s, ATA error count increased from %d to %d",
03193                    name, oldc, newc);
03194       state.must_write = true;
03195     }
03196 
03197     if (newc>=0)
03198       state.ataerrorcount=newc;
03199   }
03200 
03201   // if the user has asked, and device is capable (or we're not yet
03202   // sure) check whether a self test should be done now.
03203   if (allow_selftests && !cfg.test_regex.empty()) {
03204     char testtype = next_scheduled_test(cfg, state, false/*!scsi*/);
03205     if (testtype)
03206       DoATASelfTest(cfg, state, atadev, testtype);
03207   }
03208 
03209   // Don't leave device open -- the OS/user may want to access it
03210   // before the next smartd cycle!
03211   CloseDevice(atadev, name);
03212 
03213   // Copy ATA attribute values to persistent state
03214   state.update_persistent_state();
03215 
03216   return 0;
03217 }
03218 
03219 static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_device * scsidev, bool allow_selftests)
03220 {
03221     UINT8 asc, ascq;
03222     UINT8 currenttemp;
03223     UINT8 triptemp;
03224     UINT8  tBuf[252];
03225     const char * name = cfg.name.c_str();
03226     const char *cp;
03227 
03228     // If the user has asked for it, test the email warning system
03229     if (cfg.emailtest)
03230       MailWarning(cfg, state, 0, "TEST EMAIL from smartd for device: %s", name);
03231 
03232     // if we can't open device, fail gracefully rather than hard --
03233     // perhaps the next time around we'll be able to open it
03234     if (!scsidev->open()) {
03235       PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, scsidev->get_errmsg());
03236       MailWarning(cfg, state, 9, "Device: %s, unable to open device", name);
03237       return 1;
03238     } else if (debugmode)
03239         PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name);
03240     reset_warning_mail(cfg, state, 9, "open device worked again");
03241     currenttemp = 0;
03242     asc = 0;
03243     ascq = 0;
03244     if (!state.SuppressReport) {
03245         if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported,
03246                         &asc, &ascq, &currenttemp, &triptemp)) {
03247             PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n",
03248                       name);
03249             MailWarning(cfg, state, 6, "Device: %s, failed to read SMART values", name);
03250             state.SuppressReport = 1;
03251         }
03252     }
03253     if (asc > 0) {
03254         cp = scsiGetIEString(asc, ascq);
03255         if (cp) {
03256             PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
03257             MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp);
03258         } else if (asc == 4 && ascq == 9) {
03259             PrintOut(LOG_INFO,"Device: %s, self-test in progress\n", name);  
03260         } else if (debugmode)
03261             PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n",
03262                      name, (int)asc, (int)ascq);  
03263     } else if (debugmode)
03264         PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name);  
03265 
03266     // check temperature limits
03267     if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit || !cfg.attrlog_file.empty())
03268       CheckTemperature(cfg, state, currenttemp, triptemp);
03269 
03270     // check if number of selftest errors has increased (note: may also DECREASE)
03271     if (cfg.selftest)
03272       CheckSelfTestLogs(cfg, state, scsiCountFailedSelfTests(scsidev, 0));
03273     
03274     if (allow_selftests && !cfg.test_regex.empty()) {
03275       char testtype = next_scheduled_test(cfg, state, true/*scsi*/);
03276       if (testtype)
03277         DoSCSISelfTest(cfg, state, scsidev, testtype);
03278     }
03279     if (!cfg.attrlog_file.empty()){
03280       // saving error counters to state
03281       if (state.ReadECounterPageSupported && (0 == scsiLogSense(scsidev,
03282           READ_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
03283           scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[0].errCounter);
03284           state.scsi_error_counters[0].found=1;
03285       }
03286       if (state.WriteECounterPageSupported && (0 == scsiLogSense(scsidev,
03287           WRITE_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
03288           scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[1].errCounter);
03289           state.scsi_error_counters[1].found=1;
03290       }
03291       if (state.VerifyECounterPageSupported && (0 == scsiLogSense(scsidev,
03292           VERIFY_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
03293           scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[2].errCounter);
03294           state.scsi_error_counters[2].found=1;
03295       }
03296       if (state.NonMediumErrorPageSupported && (0 == scsiLogSense(scsidev,
03297           NON_MEDIUM_ERROR_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
03298           scsiDecodeNonMediumErrPage(tBuf, &state.scsi_nonmedium_error.nme);
03299           state.scsi_nonmedium_error.found=1;
03300       }
03301     }
03302     CloseDevice(scsidev, name);
03303     return 0;
03304 }
03305 
03306 // 0=not used, 1=not disabled, 2=disable rejected by OS, 3=disabled
03307 static int standby_disable_state = 0;
03308 
03309 static void init_disable_standby_check(dev_config_vector & configs)
03310 {
03311   // Check for '-l offlinests,ns' or '-l selfteststs,ns' directives
03312   bool sts1 = false, sts2 = false;
03313   for (unsigned i = 0; i < configs.size() && !(sts1 || sts2); i++) {
03314     const dev_config & cfg = configs.at(i);
03315     if (cfg.offlinests_ns)
03316       sts1 = true;
03317     if (cfg.selfteststs_ns)
03318       sts2 = true;
03319   }
03320 
03321   // Check for support of disable auto standby
03322   // Reenable standby if smartd.conf was reread
03323   if (sts1 || sts2 || standby_disable_state == 3) {
03324    if (!smi()->disable_system_auto_standby(false)) {
03325       if (standby_disable_state == 3)
03326         PrintOut(LOG_CRIT, "System auto standby enable failed: %s\n", smi()->get_errmsg());
03327       if (sts1 || sts2) {
03328         PrintOut(LOG_INFO, "Disable auto standby not supported, ignoring ',ns' from %s%s%s\n",
03329           (sts1 ? "-l offlinests,ns" : ""), (sts1 && sts2 ? " and " : ""), (sts2 ? "-l selfteststs,ns" : ""));
03330         sts1 = sts2 = false;
03331       }
03332     }
03333   }
03334 
03335   standby_disable_state = (sts1 || sts2 ? 1 : 0);
03336 }
03337 
03338 static void do_disable_standby_check(const dev_config_vector & configs, const dev_state_vector & states)
03339 {
03340   if (!standby_disable_state)
03341     return;
03342 
03343   // Check for just started or still running self-tests
03344   bool running = false;
03345   for (unsigned i = 0; i < configs.size() && !running; i++) {
03346     const dev_config & cfg = configs.at(i); const dev_state & state = states.at(i);
03347 
03348     if (   (   cfg.offlinests_ns
03349             && (state.offline_started ||
03350                 is_offl_coll_in_progress(state.smartval.offline_data_collection_status)))
03351         || (   cfg.selfteststs_ns
03352             && (state.selftest_started ||
03353                 is_self_test_in_progress(state.smartval.self_test_exec_status)))         )
03354       running = true;
03355     // state.offline/selftest_started will be reset after next logging of test status
03356   }
03357 
03358   // Disable/enable auto standby and log state changes
03359   if (!running) {
03360     if (standby_disable_state != 1) {
03361       if (!smi()->disable_system_auto_standby(false))
03362         PrintOut(LOG_CRIT, "Self-test(s) completed, system auto standby enable failed: %s\n",
03363                  smi()->get_errmsg());
03364       else
03365         PrintOut(LOG_INFO, "Self-test(s) completed, system auto standby enabled\n");
03366       standby_disable_state = 1;
03367     }
03368   }
03369   else if (!smi()->disable_system_auto_standby(true)) {
03370     if (standby_disable_state != 2) {
03371       PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disable rejected: %s\n",
03372                smi()->get_errmsg());
03373       standby_disable_state = 2;
03374     }
03375   }
03376   else {
03377     if (standby_disable_state != 3) {
03378       PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disabled\n");
03379       standby_disable_state = 3;
03380     }
03381   }
03382 }
03383 
03384 // Checks the SMART status of all ATA and SCSI devices
03385 static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector & states,
03386                              smart_device_list & devices, bool firstpass, bool allow_selftests)
03387 {
03388   for (unsigned i = 0; i < configs.size(); i++) {
03389     const dev_config & cfg = configs.at(i);
03390     dev_state & state = states.at(i);
03391     smart_device * dev = devices.at(i);
03392     if (dev->is_ata())
03393       ATACheckDevice(cfg, state, dev->to_ata(), firstpass, allow_selftests);
03394     else if (dev->is_scsi())
03395       SCSICheckDevice(cfg, state, dev->to_scsi(), allow_selftests);
03396   }
03397 
03398   do_disable_standby_check(configs, states);
03399 }
03400 
03401 // Set if Initialize() was called
03402 static bool is_initialized = false;
03403 
03404 // Does initialization right after fork to daemon mode
03405 static void Initialize(time_t *wakeuptime)
03406 {
03407   // Call Goodbye() on exit
03408   is_initialized = true;
03409   
03410   // write PID file
03411   if (!debugmode)
03412     WritePidFile();
03413   
03414   // install signal handlers.  On Solaris, can't use signal() because
03415   // it resets the handler to SIG_DFL after each call.  So use sigset()
03416   // instead.  So SIGNALFN()==signal() or SIGNALFN()==sigset().
03417   
03418   // normal and abnormal exit
03419   if (SIGNALFN(SIGTERM, sighandler)==SIG_IGN)
03420     SIGNALFN(SIGTERM, SIG_IGN);
03421   if (SIGNALFN(SIGQUIT, sighandler)==SIG_IGN)
03422     SIGNALFN(SIGQUIT, SIG_IGN);
03423   
03424   // in debug mode, <CONTROL-C> ==> HUP
03425   if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN)
03426     SIGNALFN(SIGINT, SIG_IGN);
03427   
03428   // Catch HUP and USR1
03429   if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN)
03430     SIGNALFN(SIGHUP, SIG_IGN);
03431   if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN)
03432     SIGNALFN(SIGUSR1, SIG_IGN);
03433 #ifdef _WIN32
03434   if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN)
03435     SIGNALFN(SIGUSR2, SIG_IGN);
03436 #endif
03437 
03438   // initialize wakeup time to CURRENT time
03439   *wakeuptime=time(NULL);
03440   
03441   return;
03442 }
03443 
03444 #ifdef _WIN32
03445 // Toggle debug mode implemented for native windows only
03446 // (there is no easy way to reopen tty on *nix)
03447 static void ToggleDebugMode()
03448 {
03449   if (!debugmode) {
03450     PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n");
03451     if (!daemon_enable_console("smartd [Debug]")) {
03452       debugmode = 1;
03453       daemon_signal(SIGINT, HUPhandler);
03454       PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid());
03455     }
03456     else
03457       PrintOut(LOG_INFO,"enable console failed\n");
03458   }
03459   else if (debugmode == 1) {
03460     daemon_disable_console();
03461     debugmode = 0;
03462     daemon_signal(SIGINT, sighandler);
03463     PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n");
03464   }
03465   else
03466     PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode);
03467 }
03468 #endif
03469 
03470 static time_t dosleep(time_t wakeuptime, bool & sigwakeup)
03471 {
03472   // If past wake-up-time, compute next wake-up-time
03473   time_t timenow=time(NULL);
03474   while (wakeuptime<=timenow){
03475     int intervals=1+(timenow-wakeuptime)/checktime;
03476     wakeuptime+=intervals*checktime;
03477   }
03478   
03479   // sleep until we catch SIGUSR1 or have completed sleeping
03480   int addtime = 0;
03481   while (timenow < wakeuptime+addtime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT) {
03482     
03483     // protect user again system clock being adjusted backwards
03484     if (wakeuptime>timenow+checktime){
03485       PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n");
03486       wakeuptime=timenow+checktime;
03487     }
03488     
03489     // Exit sleep when time interval has expired or a signal is received
03490     sleep(wakeuptime+addtime-timenow);
03491 
03492 #ifdef _WIN32
03493     // toggle debug mode?
03494     if (caughtsigUSR2) {
03495       ToggleDebugMode();
03496       caughtsigUSR2 = 0;
03497     }
03498 #endif
03499 
03500     timenow=time(NULL);
03501 
03502     // Actual sleep time too long?
03503     if (!addtime && timenow > wakeuptime+60) {
03504       if (debugmode)
03505         PrintOut(LOG_INFO, "Sleep time was %d seconds too long, assuming wakeup from standby mode.\n",
03506           (int)(timenow-wakeuptime));
03507       // Wait another 20 seconds to avoid I/O errors during disk spin-up
03508       addtime = timenow-wakeuptime+20;
03509       // Use next wake-up-time if close
03510       int nextcheck = checktime - addtime % checktime;
03511       if (nextcheck <= 20)
03512         addtime += nextcheck;
03513     }
03514   }
03515  
03516   // if we caught a SIGUSR1 then print message and clear signal
03517   if (caughtsigUSR1){
03518     PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n",
03519              wakeuptime-timenow>0?(int)(wakeuptime-timenow):0);
03520     caughtsigUSR1=0;
03521     sigwakeup = true;
03522   }
03523   
03524   // return adjusted wakeuptime
03525   return wakeuptime;
03526 }
03527 
03528 // Print out a list of valid arguments for the Directive d
03529 static void printoutvaliddirectiveargs(int priority, char d)
03530 {
03531   switch (d) {
03532   case 'n':
03533     PrintOut(priority, "never[,N][,q], sleep[,N][,q], standby[,N][,q], idle[,N][,q]");
03534     break;
03535   case 's':
03536     PrintOut(priority, "valid_regular_expression");
03537     break;
03538   case 'd':
03539     PrintOut(priority, "%s", smi()->get_valid_dev_types_str().c_str());
03540     break;
03541   case 'T':
03542     PrintOut(priority, "normal, permissive");
03543     break;
03544   case 'o':
03545   case 'S':
03546     PrintOut(priority, "on, off");
03547     break;
03548   case 'l':
03549     PrintOut(priority, "error, selftest");
03550     break;
03551   case 'M':
03552     PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\"");
03553     break;
03554   case 'v':
03555     PrintOut(priority, "\n%s\n", create_vendor_attribute_arg_list().c_str());
03556     break;
03557   case 'P':
03558     PrintOut(priority, "use, ignore, show, showall");
03559     break;
03560   case 'F':
03561     PrintOut(priority, "%s", get_valid_firmwarebug_args());
03562     break;
03563   case 'e':
03564     PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], "
03565                        "security-freeze, standby,[N|off], wcache,[on|off]");
03566     break;
03567   }
03568 }
03569 
03570 // exits with an error message, or returns integer value of token
03571 static int GetInteger(const char *arg, const char *name, const char *token, int lineno, const char *cfgfile,
03572                int min, int max, char * suffix = 0)
03573 {
03574   // make sure argument is there
03575   if (!arg) {
03576     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n",
03577              cfgfile, lineno, name, token, min, max);
03578     return -1;
03579   }
03580   
03581   // get argument value (base 10), check that it's integer, and in-range
03582   char *endptr;
03583   int val = strtol(arg,&endptr,10);
03584 
03585   // optional suffix present?
03586   if (suffix) {
03587     if (!strcmp(endptr, suffix))
03588       endptr += strlen(suffix);
03589     else
03590       *suffix = 0;
03591   }
03592 
03593   if (!(!*endptr && min <= val && val <= max)) {
03594     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n",
03595              cfgfile, lineno, name, token, arg, min, max);
03596     return -1;
03597   }
03598 
03599   // all is well; return value
03600   return val;
03601 }
03602 
03603 
03604 // Get 1-3 small integer(s) for '-W' directive
03605 static int Get3Integers(const char *arg, const char *name, const char *token, int lineno, const char *cfgfile,
03606                  unsigned char *val1, unsigned char *val2, unsigned char *val3)
03607 {
03608   unsigned v1 = 0, v2 = 0, v3 = 0;
03609   int n1 = -1, n2 = -1, n3 = -1, len;
03610   if (!arg) {
03611     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes 1-3 integer argument(s) from 0 to 255.\n",
03612              cfgfile, lineno, name, token);
03613     return -1;
03614   }
03615 
03616   len = strlen(arg);
03617   if (!(   sscanf(arg, "%u%n,%u%n,%u%n", &v1, &n1, &v2, &n2, &v3, &n3) >= 1
03618         && (n1 == len || n2 == len || n3 == len) && v1 <= 255 && v2 <= 255 && v3 <= 255)) {
03619     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs 1-3 integer(s) from 0 to 255.\n",
03620              cfgfile, lineno, name, token, arg);
03621     return -1;
03622   }
03623   *val1 = (unsigned char)v1; *val2 = (unsigned char)v2; *val3 = (unsigned char)v3;
03624   return 0;
03625 }
03626 
03627 
03628 #ifdef _WIN32
03629 
03630 // Concatenate strtok() results if quoted with "..."
03631 static const char * strtok_dequote(const char * delimiters)
03632 {
03633   const char * t = strtok(0, delimiters);
03634   if (!t || t[0] != '"')
03635     return t;
03636 
03637   static std::string token;
03638   token = t+1;
03639   for (;;) {
03640     t = strtok(0, delimiters);
03641     if (!t || !*t)
03642       return "\"";
03643     token += ' ';
03644     int len = strlen(t);
03645     if (t[len-1] == '"') {
03646       token += std::string(t, len-1);
03647       break;
03648     }
03649     token += t;
03650   }
03651   return token.c_str();
03652 }
03653 
03654 #endif // _WIN32
03655 
03656 
03657 // This function returns 1 if it has correctly parsed one token (and
03658 // any arguments), else zero if no tokens remain.  It returns -1 if an
03659 // error was encountered.
03660 static int ParseToken(char * token, dev_config & cfg)
03661 {
03662   char sym;
03663   const char * name = cfg.name.c_str();
03664   int lineno=cfg.lineno;
03665   const char *delim = " \n\t";
03666   int badarg = 0;
03667   int missingarg = 0;
03668   const char *arg = 0;
03669 
03670   // is the rest of the line a comment
03671   if (*token=='#')
03672     return 1;
03673   
03674   // is the token not recognized?
03675   if (*token!='-' || strlen(token)!=2) {
03676     PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
03677              configfile, lineno, name, token);
03678     PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n");
03679     return -1;
03680   }
03681   
03682   // token we will be parsing:
03683   sym=token[1];
03684 
03685   // parse the token and swallow its argument
03686   int val;
03687   char plus[] = "+", excl[] = "!";
03688 
03689   switch (sym) {
03690   case 'C':
03691     // monitor current pending sector count (default 197)
03692     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 0)
03693       return -1;
03694     cfg.curr_pending_id = (unsigned char)val;
03695     cfg.curr_pending_incr = (*plus == '+');
03696     cfg.curr_pending_set = true;
03697     break;
03698   case 'U':
03699     // monitor offline uncorrectable sectors (default 198)
03700     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 0)
03701       return -1;
03702     cfg.offl_pending_id = (unsigned char)val;
03703     cfg.offl_pending_incr = (*plus == '+');
03704     cfg.offl_pending_set = true;
03705     break;
03706   case 'T':
03707     // Set tolerance level for SMART command failures
03708     if ((arg = strtok(NULL, delim)) == NULL) {
03709       missingarg = 1;
03710     } else if (!strcmp(arg, "normal")) {
03711       // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but
03712       // not on failure of an optional S.M.A.R.T. command.
03713       // This is the default so we don't need to actually do anything here.
03714       cfg.permissive = false;
03715     } else if (!strcmp(arg, "permissive")) {
03716       // Permissive mode; ignore errors from Mandatory SMART commands
03717       cfg.permissive = true;
03718     } else {
03719       badarg = 1;
03720     }
03721     break;
03722   case 'd':
03723     // specify the device type
03724     if ((arg = strtok(NULL, delim)) == NULL) {
03725       missingarg = 1;
03726     } else if (!strcmp(arg, "ignore")) {
03727       cfg.ignore = true;
03728     } else if (!strcmp(arg, "removable")) {
03729       cfg.removable = true;
03730     } else if (!strcmp(arg, "auto")) {
03731       cfg.dev_type = "";
03732     } else {
03733       cfg.dev_type = arg;
03734     }
03735     break;
03736   case 'F':
03737     // fix firmware bug
03738     if (!(arg = strtok(0, delim)))
03739       missingarg = 1;
03740     else if (!parse_firmwarebug_def(arg, cfg.firmwarebugs))
03741       badarg = 1;
03742     break;
03743   case 'H':
03744     // check SMART status
03745     cfg.smartcheck = true;
03746     break;
03747   case 'f':
03748     // check for failure of usage attributes
03749     cfg.usagefailed = true;
03750     break;
03751   case 't':
03752     // track changes in all vendor attributes
03753     cfg.prefail = true;
03754     cfg.usage = true;
03755     break;
03756   case 'p':
03757     // track changes in prefail vendor attributes
03758     cfg.prefail = true;
03759     break;
03760   case 'u':
03761     //  track changes in usage vendor attributes
03762     cfg.usage = true;
03763     break;
03764   case 'l':
03765     // track changes in SMART logs
03766     if ((arg = strtok(NULL, delim)) == NULL) {
03767       missingarg = 1;
03768     } else if (!strcmp(arg, "selftest")) {
03769       // track changes in self-test log
03770       cfg.selftest = true;
03771     } else if (!strcmp(arg, "error")) {
03772       // track changes in ATA error log
03773       cfg.errorlog = true;
03774     } else if (!strcmp(arg, "xerror")) {
03775       // track changes in Extended Comprehensive SMART error log
03776       cfg.xerrorlog = true;
03777     } else if (!strcmp(arg, "offlinests")) {
03778       // track changes in offline data collection status
03779       cfg.offlinests = true;
03780     } else if (!strcmp(arg, "offlinests,ns")) {
03781       // track changes in offline data collection status, disable auto standby
03782       cfg.offlinests = cfg.offlinests_ns = true;
03783     } else if (!strcmp(arg, "selfteststs")) {
03784       // track changes in self-test execution status
03785       cfg.selfteststs = true;
03786     } else if (!strcmp(arg, "selfteststs,ns")) {
03787       // track changes in self-test execution status, disable auto standby
03788       cfg.selfteststs = cfg.selfteststs_ns = true;
03789     } else if (!strncmp(arg, "scterc,", sizeof("scterc,")-1)) {
03790         // set SCT Error Recovery Control
03791         unsigned rt = ~0, wt = ~0; int nc = -1;
03792         sscanf(arg,"scterc,%u,%u%n", &rt, &wt, &nc);
03793         if (nc == (int)strlen(arg) && rt <= 999 && wt <= 999) {
03794           cfg.sct_erc_set = true;
03795           cfg.sct_erc_readtime = rt;
03796           cfg.sct_erc_writetime = wt;
03797         }
03798         else
03799           badarg = 1;
03800     } else {
03801       badarg = 1;
03802     }
03803     break;
03804   case 'a':
03805     // monitor everything
03806     cfg.smartcheck = true;
03807     cfg.prefail = true;
03808     cfg.usagefailed = true;
03809     cfg.usage = true;
03810     cfg.selftest = true;
03811     cfg.errorlog = true;
03812     cfg.selfteststs = true;
03813     break;
03814   case 'o':
03815     // automatic offline testing enable/disable
03816     if ((arg = strtok(NULL, delim)) == NULL) {
03817       missingarg = 1;
03818     } else if (!strcmp(arg, "on")) {
03819       cfg.autoofflinetest = 2;
03820     } else if (!strcmp(arg, "off")) {
03821       cfg.autoofflinetest = 1;
03822     } else {
03823       badarg = 1;
03824     }
03825     break;
03826   case 'n':
03827     // skip disk check if in idle or standby mode
03828     if (!(arg = strtok(NULL, delim)))
03829       missingarg = 1;
03830     else {
03831       char *endptr = NULL;
03832       char *next = strchr(const_cast<char*>(arg), ',');
03833 
03834       cfg.powerquiet = false;
03835       cfg.powerskipmax = 0;
03836 
03837       if (next!=NULL) *next='\0';
03838       if (!strcmp(arg, "never"))
03839         cfg.powermode = 0;
03840       else if (!strcmp(arg, "sleep"))
03841         cfg.powermode = 1;
03842       else if (!strcmp(arg, "standby"))
03843         cfg.powermode = 2;
03844       else if (!strcmp(arg, "idle"))
03845         cfg.powermode = 3;
03846       else
03847         badarg = 1;
03848 
03849       // if optional arguments are present
03850       if (!badarg && next!=NULL) {
03851         next++;
03852         cfg.powerskipmax = strtol(next, &endptr, 10);
03853         if (endptr == next)
03854           cfg.powerskipmax = 0;
03855         else {
03856           next = endptr + (*endptr != '\0');
03857           if (cfg.powerskipmax <= 0)
03858             badarg = 1;
03859         }
03860         if (*next != '\0') {
03861           if (!strcmp("q", next))
03862             cfg.powerquiet = true;
03863           else {
03864             badarg = 1;
03865           }
03866         }
03867       }
03868     }
03869     break;
03870   case 'S':
03871     // automatic attribute autosave enable/disable
03872     if ((arg = strtok(NULL, delim)) == NULL) {
03873       missingarg = 1;
03874     } else if (!strcmp(arg, "on")) {
03875       cfg.autosave = 2;
03876     } else if (!strcmp(arg, "off")) {
03877       cfg.autosave = 1;
03878     } else {
03879       badarg = 1;
03880     }
03881     break;
03882   case 's':
03883     // warn user, and delete any previously given -s REGEXP Directives
03884     if (!cfg.test_regex.empty()){
03885       PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n",
03886                configfile, lineno, name, cfg.test_regex.get_pattern());
03887       cfg.test_regex = regular_expression();
03888     }
03889     // check for missing argument
03890     if (!(arg = strtok(NULL, delim))) {
03891       missingarg = 1;
03892     }
03893     // Compile regex
03894     else {
03895       if (!cfg.test_regex.compile(arg, REG_EXTENDED)) {
03896         // not a valid regular expression!
03897         PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
03898                  configfile, lineno, name, arg, cfg.test_regex.get_errmsg());
03899         return -1;
03900       }
03901     }
03902     // Do a bit of sanity checking and warn user if we think that
03903     // their regexp is "strange". User probably confused about shell
03904     // glob(3) syntax versus regular expression syntax regexp(7).
03905     if (arg[(val = strspn(arg, "0123456789/.-+*|()?^$[]SLCOcnr"))])
03906       PrintOut(LOG_INFO,  "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
03907                configfile, lineno, name, val+1, arg[val], arg);
03908     break;
03909   case 'm':
03910     // send email to address that follows
03911     if (!(arg = strtok(NULL,delim)))
03912       missingarg = 1;
03913     else {
03914       if (!cfg.emailaddress.empty())
03915         PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
03916                  configfile, lineno, name, cfg.emailaddress.c_str());
03917 #ifdef _WIN32
03918       if (   !strcmp(arg, "msgbox")          || !strcmp(arg, "sysmsgbox")
03919           || str_starts_with(arg, "msgbox,") || str_starts_with(arg, "sysmsgbox,")) {
03920         cfg.emailaddress = "console";
03921         const char * arg2 = strchr(arg, ',');
03922         if (arg2)
03923           cfg.emailaddress += arg2;
03924         PrintOut(LOG_INFO, "File %s line %d (drive %s): Deprecated -m %s changed to -m %s\n",
03925                  configfile, lineno, name, arg, cfg.emailaddress.c_str());
03926       }
03927       else
03928 #endif
03929       cfg.emailaddress = arg;
03930     }
03931     break;
03932   case 'M':
03933     // email warning options
03934     if (!(arg = strtok(NULL, delim)))
03935       missingarg = 1;
03936     else if (!strcmp(arg, "once"))
03937       cfg.emailfreq = 1;
03938     else if (!strcmp(arg, "daily"))
03939       cfg.emailfreq = 2;
03940     else if (!strcmp(arg, "diminishing"))
03941       cfg.emailfreq = 3;
03942     else if (!strcmp(arg, "test"))
03943       cfg.emailtest = 1;
03944     else if (!strcmp(arg, "exec")) {
03945       // Get the next argument (the command line)
03946 #ifdef _WIN32
03947       // Allow "/path name/with spaces/..." on Windows
03948       arg = strtok_dequote(delim);
03949       if (arg && arg[0] == '"') {
03950         PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument: missing closing quote\n",
03951                  configfile, lineno, name, token);
03952         return -1;
03953       }
03954 #else
03955       arg = strtok(0, delim);
03956 #endif
03957       if (!arg) {
03958         PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n",
03959                  configfile, lineno, name, token);
03960         return -1;
03961       }
03962       // Free the last cmd line given if any, and copy new one
03963       if (!cfg.emailcmdline.empty())
03964         PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n",
03965                  configfile, lineno, name, cfg.emailcmdline.c_str());
03966       cfg.emailcmdline = arg;
03967     } 
03968     else
03969       badarg = 1;
03970     break;
03971   case 'i':
03972     // ignore failure of usage attribute
03973     if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
03974       return -1;
03975     cfg.monitor_attr_flags.set(val, MONITOR_IGN_FAILUSE);
03976     break;
03977   case 'I':
03978     // ignore attribute for tracking purposes
03979     if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
03980       return -1;
03981     cfg.monitor_attr_flags.set(val, MONITOR_IGNORE);
03982     break;
03983   case 'r':
03984     // print raw value when tracking
03985     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255, excl)) < 0)
03986       return -1;
03987     cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT);
03988     if (*excl == '!') // attribute change is critical
03989       cfg.monitor_attr_flags.set(val, MONITOR_AS_CRIT);
03990     break;
03991   case 'R':
03992     // track changes in raw value (forces printing of raw value)
03993     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255, excl)) < 0)
03994       return -1;
03995     cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT|MONITOR_RAW);
03996     if (*excl == '!') // raw value change is critical
03997       cfg.monitor_attr_flags.set(val, MONITOR_RAW_AS_CRIT);
03998     break;
03999   case 'W':
04000     // track Temperature
04001     if ((val=Get3Integers(arg=strtok(NULL,delim), name, token, lineno, configfile,
04002                           &cfg.tempdiff, &cfg.tempinfo, &cfg.tempcrit))<0)
04003       return -1;
04004     break;
04005   case 'v':
04006     // non-default vendor-specific attribute meaning
04007     if (!(arg=strtok(NULL,delim))) {
04008       missingarg = 1;
04009     } else if (!parse_attribute_def(arg, cfg.attribute_defs, PRIOR_USER)) {
04010       badarg = 1;
04011     }
04012     break;
04013   case 'P':
04014     // Define use of drive-specific presets.
04015     if (!(arg = strtok(NULL, delim))) {
04016       missingarg = 1;
04017     } else if (!strcmp(arg, "use")) {
04018       cfg.ignorepresets = false;
04019     } else if (!strcmp(arg, "ignore")) {
04020       cfg.ignorepresets = true;
04021     } else if (!strcmp(arg, "show")) {
04022       cfg.showpresets = true;
04023     } else if (!strcmp(arg, "showall")) {
04024       showallpresets();
04025     } else {
04026       badarg = 1;
04027     }
04028     break;
04029 
04030   case 'e':
04031     // Various ATA settings
04032     if (!(arg = strtok(NULL, delim))) {
04033       missingarg = true;
04034     }
04035     else {
04036       char arg2[16+1]; unsigned val;
04037       int n1 = -1, n2 = -1, n3 = -1, len = strlen(arg);
04038       if (sscanf(arg, "%16[^,=]%n%*[,=]%n%u%n", arg2, &n1, &n2, &val, &n3) >= 1
04039           && (n1 == len || n2 > 0)) {
04040         bool on  = (n2 > 0 && !strcmp(arg+n2, "on"));
04041         bool off = (n2 > 0 && !strcmp(arg+n2, "off"));
04042         if (n3 != len)
04043           val = ~0U;
04044 
04045         if (!strcmp(arg2, "aam")) {
04046           if (off)
04047             cfg.set_aam = -1;
04048           else if (val <= 254)
04049             cfg.set_aam = val + 1;
04050           else
04051             badarg = true;
04052         }
04053         else if (!strcmp(arg2, "apm")) {
04054           if (off)
04055             cfg.set_apm = -1;
04056           else if (1 <= val && val <= 254)
04057             cfg.set_apm = val + 1;
04058           else
04059             badarg = true;
04060         }
04061         else if (!strcmp(arg2, "lookahead")) {
04062           if (off)
04063             cfg.set_lookahead = -1;
04064           else if (on)
04065             cfg.set_lookahead = 1;
04066           else
04067             badarg = true;
04068         }
04069         else if (!strcmp(arg, "security-freeze")) {
04070           cfg.set_security_freeze = true;
04071         }
04072         else if (!strcmp(arg2, "standby")) {
04073           if (off)
04074             cfg.set_standby = 0 + 1;
04075           else if (val <= 255)
04076             cfg.set_standby = val + 1;
04077           else
04078             badarg = true;
04079         }
04080         else if (!strcmp(arg2, "wcache")) {
04081           if (off)
04082             cfg.set_wcache = -1;
04083           else if (on)
04084             cfg.set_wcache = 1;
04085           else
04086             badarg = true;
04087         }
04088         else
04089           badarg = true;
04090       }
04091       else
04092         badarg = true;
04093     }
04094     break;
04095 
04096   default:
04097     // Directive not recognized
04098     PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
04099              configfile, lineno, name, token);
04100     Directives();
04101     return -1;
04102   }
04103   if (missingarg) {
04104     PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n",
04105              configfile, lineno, name, token);
04106   }
04107   if (badarg) {
04108     PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n",
04109              configfile, lineno, name, token, arg);
04110   }
04111   if (missingarg || badarg) {
04112     PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token);
04113     printoutvaliddirectiveargs(LOG_CRIT, sym);
04114     PrintOut(LOG_CRIT, "\n");
04115     return -1;
04116   }
04117 
04118   return 1;
04119 }
04120 
04121 // Scan directive for configuration file
04122 #define SCANDIRECTIVE "DEVICESCAN"
04123 
04124 // This is the routine that adds things to the conf_entries list.
04125 //
04126 // Return values are:
04127 //  1: parsed a normal line
04128 //  0: found DEFAULT setting or comment or blank line
04129 // -1: found SCANDIRECTIVE line
04130 // -2: found an error
04131 //
04132 // Note: this routine modifies *line from the caller!
04133 static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & default_conf, int lineno, /*const*/ char * line)
04134 {
04135   const char *delim = " \n\t";
04136 
04137   // get first token: device name. If a comment, skip line
04138   const char * name = strtok(line, delim);
04139   if (!name || *name == '#')
04140     return 0;
04141 
04142   // Check device name for DEFAULT or DEVICESCAN
04143   int retval;
04144   if (!strcmp("DEFAULT", name)) {
04145     retval = 0;
04146     // Restart with empty defaults
04147     default_conf = dev_config();
04148   }
04149   else {
04150     retval = (!strcmp(SCANDIRECTIVE, name) ? -1 : 1);
04151     // Init new entry with current defaults
04152     conf_entries.push_back(default_conf);
04153   }
04154   dev_config & cfg = (retval ? conf_entries.back() : default_conf);
04155 
04156   cfg.name = name; // Later replaced by dev->get_info().info_name
04157   cfg.dev_name = name; // If DEVICESCAN later replaced by get->dev_info().dev_name
04158   cfg.lineno = lineno;
04159 
04160   // parse tokens one at a time from the file.
04161   while (char * token = strtok(0, delim)) {
04162     int rc = ParseToken(token, cfg);
04163     if (rc < 0)
04164       // error found on the line
04165       return -2;
04166 
04167     if (rc == 0)
04168       // No tokens left
04169       break;
04170 
04171     // PrintOut(LOG_INFO,"Parsed token %s\n",token);
04172   }
04173 
04174   // Don't perform checks below for DEFAULT entries
04175   if (retval == 0)
04176     return retval;
04177 
04178   // If NO monitoring directives are set, then set all of them.
04179   if (!(   cfg.smartcheck  || cfg.selftest
04180         || cfg.errorlog    || cfg.xerrorlog
04181         || cfg.offlinests  || cfg.selfteststs
04182         || cfg.usagefailed || cfg.prefail  || cfg.usage
04183         || cfg.tempdiff    || cfg.tempinfo || cfg.tempcrit)) {
04184     
04185     PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n",
04186              cfg.name.c_str(), cfg.lineno, configfile);
04187     
04188     cfg.smartcheck = true;
04189     cfg.usagefailed = true;
04190     cfg.prefail = true;
04191     cfg.usage = true;
04192     cfg.selftest = true;
04193     cfg.errorlog = true;
04194     cfg.selfteststs = true;
04195   }
04196   
04197   // additional sanity check. Has user set -M options without -m?
04198   if (cfg.emailaddress.empty() && (!cfg.emailcmdline.empty() || cfg.emailfreq || cfg.emailtest)){
04199     PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n",
04200              cfg.name.c_str(), cfg.lineno, configfile);
04201     return -2;
04202   }
04203   
04204   // has the user has set <nomailer>?
04205   if (cfg.emailaddress == "<nomailer>") {
04206     // check that -M exec is also set
04207     if (cfg.emailcmdline.empty()){
04208       PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n",
04209                cfg.name.c_str(), cfg.lineno, configfile);
04210       return -2;
04211     }
04212     // From here on the sign of <nomailer> is cfg.emailaddress.empty() and !cfg.emailcmdline.empty()
04213     cfg.emailaddress.clear();
04214   }
04215 
04216   return retval;
04217 }
04218 
04219 // Parses a configuration file.  Return values are:
04220 //  N=>0: found N entries
04221 // -1:    syntax error in config file
04222 // -2:    config file does not exist
04223 // -3:    config file exists but cannot be read
04224 //
04225 // In the case where the return value is 0, there are three
04226 // possiblities:
04227 // Empty configuration file ==> conf_entries.empty()
04228 // No configuration file    ==> conf_entries[0].lineno == 0
04229 // SCANDIRECTIVE found      ==> conf_entries.back().lineno != 0 (size >= 1)
04230 static int ParseConfigFile(dev_config_vector & conf_entries)
04231 {
04232   // maximum line length in configuration file
04233   const int MAXLINELEN = 256;
04234   // maximum length of a continued line in configuration file
04235   const int MAXCONTLINE = 1023;
04236 
04237   stdio_file f;
04238   // Open config file, if it exists and is not <stdin>
04239   if (!(configfile == configfile_stdin)) { // pointer comparison ok here
04240     if (!f.open(configfile,"r") && (errno!=ENOENT || !configfile_alt.empty())) {
04241       // file exists but we can't read it or it should exist due to '-c' option
04242       int ret = (errno!=ENOENT ? -3 : -2);
04243       PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n",
04244                strerror(errno),configfile);
04245       return ret;
04246     }
04247   }
04248   else // read from stdin ('-c -' option)
04249     f.open(stdin);
04250 
04251   // Start with empty defaults
04252   dev_config default_conf;
04253 
04254   // No configuration file found -- use fake one
04255   int entry = 0;
04256   if (!f) {
04257     char fakeconfig[] = SCANDIRECTIVE " -a"; // TODO: Remove this hack, build cfg_entry.
04258 
04259     if (ParseConfigLine(conf_entries, default_conf, 0, fakeconfig) != -1)
04260       throw std::logic_error("Internal error parsing " SCANDIRECTIVE);
04261     return 0;
04262   }
04263 
04264 #ifdef __CYGWIN__
04265   setmode(fileno(f), O_TEXT); // Allow files with \r\n
04266 #endif
04267 
04268   // configuration file exists
04269   PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile);
04270 
04271   // parse config file line by line
04272   int lineno = 1, cont = 0, contlineno = 0;
04273   char line[MAXLINELEN+2];
04274   char fullline[MAXCONTLINE+1];
04275 
04276   for (;;) {
04277     int len=0,scandevice;
04278     char *lastslash;
04279     char *comment;
04280     char *code;
04281 
04282     // make debugging simpler
04283     memset(line,0,sizeof(line));
04284 
04285     // get a line
04286     code=fgets(line, MAXLINELEN+2, f);
04287     
04288     // are we at the end of the file?
04289     if (!code){
04290       if (cont) {
04291         scandevice = ParseConfigLine(conf_entries, default_conf, contlineno, fullline);
04292         // See if we found a SCANDIRECTIVE directive
04293         if (scandevice==-1)
04294           return 0;
04295         // did we find a syntax error
04296         if (scandevice==-2)
04297           return -1;
04298         // the final line is part of a continuation line
04299         cont=0;
04300         entry+=scandevice;
04301       }
04302       break;
04303     }
04304 
04305     // input file line number
04306     contlineno++;
04307     
04308     // See if line is too long
04309     len=strlen(line);
04310     if (len>MAXLINELEN){
04311       const char *warn;
04312       if (line[len-1]=='\n')
04313         warn="(including newline!) ";
04314       else
04315         warn="";
04316       PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n",
04317                (int)contlineno,configfile,warn,(int)MAXLINELEN);
04318       return -1;
04319     }
04320 
04321     // Ignore anything after comment symbol
04322     if ((comment=strchr(line,'#'))){
04323       *comment='\0';
04324       len=strlen(line);
04325     }
04326 
04327     // is the total line (made of all continuation lines) too long?
04328     if (cont+len>MAXCONTLINE){
04329       PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n",
04330                lineno, (int)contlineno, configfile, (int)MAXCONTLINE);
04331       return -1;
04332     }
04333     
04334     // copy string so far into fullline, and increment length
04335     snprintf(fullline+cont, sizeof(fullline)-cont, "%s" ,line);
04336     cont+=len;
04337 
04338     // is this a continuation line.  If so, replace \ by space and look at next line
04339     if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){
04340       *(fullline+(cont-len)+(lastslash-line))=' ';
04341       continue;
04342     }
04343 
04344     // Not a continuation line. Parse it
04345     scandevice = ParseConfigLine(conf_entries, default_conf, contlineno, fullline);
04346 
04347     // did we find a scandevice directive?
04348     if (scandevice==-1)
04349       return 0;
04350     // did we find a syntax error
04351     if (scandevice==-2)
04352       return -1;
04353 
04354     entry+=scandevice;
04355     lineno++;
04356     cont=0;
04357   }
04358 
04359   // note -- may be zero if syntax of file OK, but no valid entries!
04360   return entry;
04361 }
04362 
04363 /* Prints the message "=======> VALID ARGUMENTS ARE: <LIST>  <=======\n", where
04364    <LIST> is the list of valid arguments for option opt. */
04365 static void PrintValidArgs(char opt)
04366 {
04367   const char *s;
04368 
04369   PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: ");
04370   if (!(s = GetValidArgList(opt)))
04371     PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt);
04372   else
04373     PrintOut(LOG_CRIT, "%s", (char *)s);
04374   PrintOut(LOG_CRIT, " <=======\n");
04375 }
04376 
04377 #ifndef _WIN32
04378 // Report error and exit if specified path is not absolute.
04379 static void check_abs_path(char option, const std::string & path)
04380 {
04381   if (path.empty() || path[0] == '/')
04382     return;
04383 
04384   debugmode = 1;
04385   PrintHead();
04386   PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <=======\n\n", option, path.c_str());
04387   PrintOut(LOG_CRIT, "Error: relative path names are not allowed\n\n");
04388   EXIT(EXIT_BADCMD);
04389 }
04390 #endif // !_WIN32
04391 
04392 // Parses input line, prints usage message and
04393 // version/license/copyright messages
04394 static void ParseOpts(int argc, char **argv)
04395 {
04396   // Init default path names
04397 #ifndef _WIN32
04398   configfile = SMARTMONTOOLS_SYSCONFDIR"/smartd.conf";
04399   warning_script = SMARTMONTOOLS_SYSCONFDIR"/smartd_warning.sh";
04400 #else
04401   std::string exedir = get_exe_dir();
04402   static std::string configfile_str = exedir + "/smartd.conf";
04403   configfile = configfile_str.c_str();
04404   warning_script = exedir + "/smartd_warning.cmd";
04405 #endif
04406 
04407   // Please update GetValidArgList() if you edit shortopts
04408   static const char shortopts[] = "c:l:q:dDni:p:r:s:A:B:w:Vh?"
04409 #ifdef HAVE_LIBCAP_NG
04410                                                           "C"
04411 #endif
04412                                                              ;
04413   // Please update GetValidArgList() if you edit longopts
04414   struct option longopts[] = {
04415     { "configfile",     required_argument, 0, 'c' },
04416     { "logfacility",    required_argument, 0, 'l' },
04417     { "quit",           required_argument, 0, 'q' },
04418     { "debug",          no_argument,       0, 'd' },
04419     { "showdirectives", no_argument,       0, 'D' },
04420     { "interval",       required_argument, 0, 'i' },
04421 #ifndef _WIN32
04422     { "no-fork",        no_argument,       0, 'n' },
04423 #else
04424     { "service",        no_argument,       0, 'n' },
04425 #endif
04426     { "pidfile",        required_argument, 0, 'p' },
04427     { "report",         required_argument, 0, 'r' },
04428     { "savestates",     required_argument, 0, 's' },
04429     { "attributelog",   required_argument, 0, 'A' },
04430     { "drivedb",        required_argument, 0, 'B' },
04431     { "warnexec",       required_argument, 0, 'w' },
04432     { "version",        no_argument,       0, 'V' },
04433     { "license",        no_argument,       0, 'V' },
04434     { "copyright",      no_argument,       0, 'V' },
04435     { "help",           no_argument,       0, 'h' },
04436     { "usage",          no_argument,       0, 'h' },
04437 #ifdef HAVE_LIBCAP_NG
04438     { "capabilities",   no_argument,       0, 'C' },
04439 #endif
04440     { 0,                0,                 0, 0   }
04441   };
04442 
04443   opterr=optopt=0;
04444   bool badarg = false;
04445   bool no_defaultdb = false; // set true on '-B FILE'
04446 
04447   // Parse input options.
04448   int optchar;
04449   while ((optchar = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
04450     char *arg;
04451     char *tailptr;
04452     long lchecktime;
04453 
04454     switch(optchar) {
04455     case 'q':
04456       // when to quit
04457       if (!(strcmp(optarg,"nodev"))) {
04458         quit=0;
04459       } else if (!(strcmp(optarg,"nodevstartup"))) {
04460         quit=1;
04461       } else if (!(strcmp(optarg,"never"))) {
04462         quit=2;
04463       } else if (!(strcmp(optarg,"onecheck"))) {
04464         quit=3;
04465         debugmode=1;
04466       } else if (!(strcmp(optarg,"showtests"))) {
04467         quit=4;
04468         debugmode=1;
04469       } else if (!(strcmp(optarg,"errors"))) {
04470         quit=5;
04471       } else {
04472         badarg = true;
04473       }
04474       break;
04475     case 'l':
04476       // set the log facility level
04477       if (!strcmp(optarg, "daemon"))
04478         facility=LOG_DAEMON;
04479       else if (!strcmp(optarg, "local0"))
04480         facility=LOG_LOCAL0;
04481       else if (!strcmp(optarg, "local1"))
04482         facility=LOG_LOCAL1;
04483       else if (!strcmp(optarg, "local2"))
04484         facility=LOG_LOCAL2;
04485       else if (!strcmp(optarg, "local3"))
04486         facility=LOG_LOCAL3;
04487       else if (!strcmp(optarg, "local4"))
04488         facility=LOG_LOCAL4;
04489       else if (!strcmp(optarg, "local5"))
04490         facility=LOG_LOCAL5;
04491       else if (!strcmp(optarg, "local6"))
04492         facility=LOG_LOCAL6;
04493       else if (!strcmp(optarg, "local7"))
04494         facility=LOG_LOCAL7;
04495       else
04496         badarg = true;
04497       break;
04498     case 'd':
04499       // enable debug mode
04500       debugmode = 1;
04501       break;
04502     case 'n':
04503       // don't fork()
04504 #ifndef _WIN32 // On Windows, --service is already handled by daemon_main()
04505       do_fork = false;
04506 #endif
04507       break;
04508     case 'D':
04509       // print summary of all valid directives
04510       debugmode = 1;
04511       Directives();
04512       EXIT(0);
04513       break;
04514     case 'i':
04515       // Period (time interval) for checking
04516       // strtol will set errno in the event of overflow, so we'll check it.
04517       errno = 0;
04518       lchecktime = strtol(optarg, &tailptr, 10);
04519       if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) {
04520         debugmode=1;
04521         PrintHead();
04522         PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg);
04523         PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX);
04524         PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
04525         EXIT(EXIT_BADCMD);
04526       }
04527       checktime = (int)lchecktime;
04528       break;
04529     case 'r':
04530       // report IOCTL transactions
04531       {
04532         int i;
04533         char *s;
04534 
04535         // split_report_arg() may modify its first argument string, so use a
04536         // copy of optarg in case we want optarg for an error message.
04537         if (!(s = strdup(optarg))) {
04538           PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n");
04539           EXIT(EXIT_NOMEM);
04540         }
04541         if (split_report_arg(s, &i)) {
04542           badarg = true;
04543         } else if (i<1 || i>3) {
04544           debugmode=1;
04545           PrintHead();
04546           PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg);
04547           PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n");
04548           EXIT(EXIT_BADCMD);
04549         } else if (!strcmp(s,"ioctl")) {
04550           ata_debugmode = scsi_debugmode = i;
04551         } else if (!strcmp(s,"ataioctl")) {
04552           ata_debugmode = i;
04553         } else if (!strcmp(s,"scsiioctl")) {
04554           scsi_debugmode = i;
04555         } else {
04556           badarg = true;
04557         }
04558         free(s);  // TODO: use std::string
04559       }
04560       break;
04561     case 'c':
04562       // alternate configuration file
04563       if (strcmp(optarg,"-"))
04564         configfile = (configfile_alt = optarg).c_str();
04565       else // read from stdin
04566         configfile=configfile_stdin;
04567       break;
04568     case 'p':
04569       // output file with PID number
04570       pid_file = optarg;
04571       break;
04572     case 's':
04573       // path prefix of persistent state file
04574       state_path_prefix = optarg;
04575       break;
04576     case 'A':
04577       // path prefix of attribute log file
04578       attrlog_path_prefix = optarg;
04579       break;
04580     case 'B':
04581       {
04582         const char * path = optarg;
04583         if (*path == '+' && path[1])
04584           path++;
04585         else
04586           no_defaultdb = true;
04587         unsigned char savedebug = debugmode; debugmode = 1;
04588         if (!read_drive_database(path))
04589           EXIT(EXIT_BADCMD);
04590         debugmode = savedebug;
04591       }
04592       break;
04593     case 'w':
04594       warning_script = optarg;
04595       break;
04596     case 'V':
04597       // print version and CVS info
04598       debugmode = 1;
04599       PrintOut(LOG_INFO, "%s", format_version_info("smartd", true /*full*/).c_str());
04600       EXIT(0);
04601       break;
04602 #ifdef HAVE_LIBCAP_NG
04603     case 'C':
04604       // enable capabilities
04605       enable_capabilities = true;
04606       break;
04607 #endif
04608     case 'h':
04609       // help: print summary of command-line options
04610       debugmode=1;
04611       PrintHead();
04612       Usage();
04613       EXIT(0);
04614       break;
04615     case '?':
04616     default:
04617       // unrecognized option
04618       debugmode=1;
04619       PrintHead();
04620       // Point arg to the argument in which this option was found.
04621       arg = argv[optind-1];
04622       // Check whether the option is a long option that doesn't map to -h.
04623       if (arg[1] == '-' && optchar != 'h') {
04624         // Iff optopt holds a valid option then argument must be missing.
04625         if (optopt && (strchr(shortopts, optopt) != NULL)) {
04626           PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2);
04627           PrintValidArgs(optopt);
04628         } else {
04629           PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
04630         }
04631         PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n");
04632         EXIT(EXIT_BADCMD);
04633       }
04634       if (optopt) {
04635         // Iff optopt holds a valid option then argument must be missing.
04636         if (strchr(shortopts, optopt) != NULL){
04637           PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt);
04638           PrintValidArgs(optopt);
04639         } else {
04640           PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
04641         }
04642         PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
04643         EXIT(EXIT_BADCMD);
04644       }
04645       Usage();
04646       EXIT(0);
04647     }
04648 
04649     // Check to see if option had an unrecognized or incorrect argument.
04650     if (badarg) {
04651       debugmode=1;
04652       PrintHead();
04653       // It would be nice to print the actual option name given by the user
04654       // here, but we just print the short form.  Please fix this if you know
04655       // a clean way to do it.
04656       PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg);
04657       PrintValidArgs(optchar);
04658       PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
04659       EXIT(EXIT_BADCMD);
04660     }
04661   }
04662 
04663   // non-option arguments are not allowed
04664   if (argc > optind) {
04665     debugmode=1;
04666     PrintHead();
04667     PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]);
04668     PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
04669     EXIT(EXIT_BADCMD);
04670   }
04671 
04672   // no pidfile in debug mode
04673   if (debugmode && !pid_file.empty()) {
04674     debugmode=1;
04675     PrintHead();
04676     PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n");
04677     PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file.c_str());
04678     EXIT(EXIT_BADCMD);
04679   }
04680 
04681 #ifndef _WIN32
04682   if (!debugmode) {
04683     // absolute path names are required due to chdir('/') after fork().
04684     check_abs_path('p', pid_file);
04685     check_abs_path('s', state_path_prefix);
04686     check_abs_path('A', attrlog_path_prefix);
04687   }
04688 #endif
04689 
04690   // Read or init drive database
04691   if (!no_defaultdb) {
04692     unsigned char savedebug = debugmode; debugmode = 1;
04693     if (!read_default_drive_databases())
04694         EXIT(EXIT_BADCMD);
04695     debugmode = savedebug;
04696   }
04697 
04698   // print header
04699   PrintHead();
04700 }
04701 
04702 // Function we call if no configuration file was found or if the
04703 // SCANDIRECTIVE Directive was found.  It makes entries for device
04704 // names returned by scan_smart_devices() in os_OSNAME.cpp
04705 static int MakeConfigEntries(const dev_config & base_cfg,
04706   dev_config_vector & conf_entries, smart_device_list & scanned_devs, const char * type)
04707 {
04708   // make list of devices
04709   smart_device_list devlist;
04710   if (!smi()->scan_smart_devices(devlist, (*type ? type : 0)))
04711     PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
04712   
04713   // if no devices, or error constructing list, return
04714   if (devlist.size() <= 0)
04715     return 0;
04716 
04717   // add empty device slots for existing config entries
04718   while (scanned_devs.size() < conf_entries.size())
04719     scanned_devs.push_back((smart_device *)0);
04720 
04721   // loop over entries to create
04722   for (unsigned i = 0; i < devlist.size(); i++) {
04723     // Move device pointer
04724     smart_device * dev = devlist.release(i);
04725     scanned_devs.push_back(dev);
04726 
04727     // Copy configuration, update device and type name
04728     conf_entries.push_back(base_cfg);
04729     dev_config & cfg = conf_entries.back();
04730     cfg.name = dev->get_info().info_name;
04731     cfg.dev_name = dev->get_info().dev_name;
04732     cfg.dev_type = type;
04733   }
04734   
04735   return devlist.size();
04736 }
04737  
04738 static void CanNotRegister(const char *name, const char *type, int line, bool scandirective)
04739 {
04740   if (!debugmode && scandirective)
04741     return;
04742   if (line)
04743     PrintOut(scandirective?LOG_INFO:LOG_CRIT,
04744              "Unable to register %s device %s at line %d of file %s\n",
04745              type, name, line, configfile);
04746   else
04747     PrintOut(LOG_INFO,"Unable to register %s device %s\n",
04748              type, name);
04749   return;
04750 }
04751 
04752 // Returns negative value (see ParseConfigFile()) if config file
04753 // had errors, else number of entries which may be zero or positive. 
04754 static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_device_list & scanned_devs)
04755 {
04756   // parse configuration file configfile (normally /etc/smartd.conf)  
04757   int entries = ParseConfigFile(conf_entries);
04758 
04759   if (entries < 0) {
04760     // There was an error reading the configuration file.
04761     conf_entries.clear();
04762     if (entries == -1)
04763       PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile);
04764     return entries;
04765   }
04766 
04767   // no error parsing config file.
04768   if (entries) {
04769     // we did not find a SCANDIRECTIVE and did find valid entries
04770     PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile);
04771   }
04772   else if (!conf_entries.empty()) {
04773     // we found a SCANDIRECTIVE or there was no configuration file so
04774     // scan.  Configuration file's last entry contains all options
04775     // that were set
04776     dev_config first = conf_entries.back();
04777     conf_entries.pop_back();
04778 
04779     if (first.lineno)
04780       PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE);
04781     else
04782       PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile);
04783     
04784     // make config list of devices to search for
04785     MakeConfigEntries(first, conf_entries, scanned_devs, first.dev_type.c_str());
04786 
04787     // warn user if scan table found no devices
04788     if (conf_entries.empty())
04789       PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n");
04790   } 
04791   else
04792     PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile);
04793   
04794   return conf_entries.size();
04795 }
04796 
04797 // Return true if TYPE contains a RAID drive number
04798 static bool is_raid_type(const char * type)
04799 {
04800   if (str_starts_with(type, "sat,"))
04801     return false;
04802   int i;
04803   if (sscanf(type, "%*[^,],%d", &i) != 1)
04804     return false;
04805   return true;
04806 }
04807 
04808 // Return true if DEV is already in DEVICES[0..NUMDEVS) or IGNORED[*]
04809 static bool is_duplicate_device(const smart_device * dev,
04810                                 const smart_device_list & devices, unsigned numdevs,
04811                                 const dev_config_vector & ignored)
04812 {
04813   const smart_device::device_info & info1 = dev->get_info();
04814   bool is_raid1 = is_raid_type(info1.dev_type.c_str());
04815 
04816   for (unsigned i = 0; i < numdevs; i++) {
04817     const smart_device::device_info & info2 = devices.at(i)->get_info();
04818     // -d TYPE options must match if RAID drive number is specified
04819     if (   info1.dev_name == info2.dev_name
04820         && (   info1.dev_type == info2.dev_type
04821             || !is_raid1 || !is_raid_type(info2.dev_type.c_str())))
04822       return true;
04823   }
04824 
04825   for (unsigned i = 0; i < ignored.size(); i++) {
04826     const dev_config & cfg2 = ignored.at(i);
04827     if (   info1.dev_name == cfg2.dev_name
04828         && (   info1.dev_type == cfg2.dev_type
04829             || !is_raid1 || !is_raid_type(cfg2.dev_type.c_str())))
04830       return true;
04831   }
04832   return false;
04833 }
04834 
04835 // This function tries devices from conf_entries.  Each one that can be
04836 // registered is moved onto the [ata|scsi]devices lists and removed
04837 // from the conf_entries list.
04838 static void RegisterDevices(const dev_config_vector & conf_entries, smart_device_list & scanned_devs,
04839                             dev_config_vector & configs, dev_state_vector & states, smart_device_list & devices)
04840 {
04841   // start by clearing lists/memory of ALL existing devices
04842   configs.clear();
04843   devices.clear();
04844   states.clear();
04845 
04846   // Register entries
04847   dev_config_vector ignored_entries;
04848   unsigned numnoscan = 0;
04849   for (unsigned i = 0; i < conf_entries.size(); i++){
04850 
04851     dev_config cfg = conf_entries[i];
04852 
04853     if (cfg.ignore) {
04854       // Store for is_duplicate_device() check and ignore
04855       PrintOut(LOG_INFO, "Device: %s%s%s%s, ignored\n", cfg.name.c_str(),
04856                (!cfg.dev_type.empty() ? " [" : ""),
04857                cfg.dev_type.c_str(),
04858                (!cfg.dev_type.empty() ? "]" : ""));
04859       ignored_entries.push_back(cfg);
04860       continue;
04861     }
04862 
04863     // get device of appropriate type
04864     smart_device_auto_ptr dev;
04865     bool scanning = false;
04866 
04867     // Device may already be detected during devicescan
04868     if (i < scanned_devs.size()) {
04869       dev = scanned_devs.release(i);
04870       if (dev) {
04871         // Check for a preceding non-DEVICESCAN entry for the same device
04872         if (  (numnoscan || !ignored_entries.empty())
04873             && is_duplicate_device(dev.get(), devices, numnoscan, ignored_entries)) {
04874           PrintOut(LOG_INFO, "Device: %s, duplicate, ignored\n", dev->get_info_name());
04875           continue;
04876         }
04877         scanning = true;
04878       }
04879     }
04880 
04881     if (!dev) {
04882       dev = smi()->get_smart_device(cfg.name.c_str(), cfg.dev_type.c_str());
04883       if (!dev) {
04884         if (cfg.dev_type.empty())
04885           PrintOut(LOG_INFO,"Device: %s, unable to autodetect device type\n", cfg.name.c_str());
04886         else
04887           PrintOut(LOG_INFO,"Device: %s, unsupported device type '%s'\n", cfg.name.c_str(), cfg.dev_type.c_str());
04888         continue;
04889       }
04890     }
04891 
04892     // Save old info
04893     smart_device::device_info oldinfo = dev->get_info();
04894 
04895     // Open with autodetect support, may return 'better' device
04896     dev.replace( dev->autodetect_open() );
04897 
04898     // Report if type has changed
04899     if (oldinfo.dev_type != dev->get_dev_type())
04900       PrintOut(LOG_INFO,"Device: %s, type changed from '%s' to '%s'\n",
04901         cfg.name.c_str(), oldinfo.dev_type.c_str(), dev->get_dev_type());
04902 
04903     if (!dev->is_open()) {
04904       // For linux+devfs, a nonexistent device gives a strange error
04905       // message.  This makes the error message a bit more sensible.
04906       // If no debug and scanning - don't print errors
04907       if (debugmode || !scanning)
04908         PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", dev->get_info_name(), dev->get_errmsg());
04909       continue;
04910     }
04911 
04912     // Update informal name
04913     cfg.name = dev->get_info().info_name;
04914     PrintOut(LOG_INFO, "Device: %s, opened\n", cfg.name.c_str());
04915 
04916     // Prepare initial state
04917     dev_state state;
04918 
04919     // register ATA devices
04920     if (dev->is_ata()){
04921       if (ATADeviceScan(cfg, state, dev->to_ata())) {
04922         CanNotRegister(cfg.name.c_str(), "ATA", cfg.lineno, scanning);
04923         dev.reset();
04924       }
04925     }
04926     // or register SCSI devices
04927     else if (dev->is_scsi()){
04928       if (SCSIDeviceScan(cfg, state, dev->to_scsi())) {
04929         CanNotRegister(cfg.name.c_str(), "SCSI", cfg.lineno, scanning);
04930         dev.reset();
04931       }
04932     }
04933     else {
04934       PrintOut(LOG_INFO, "Device: %s, neither ATA nor SCSI device\n", cfg.name.c_str());
04935       dev.reset();
04936     }
04937 
04938     if (dev) {
04939       // move onto the list of devices
04940       configs.push_back(cfg);
04941       states.push_back(state);
04942       devices.push_back(dev);
04943       if (!scanning)
04944         numnoscan = devices.size();
04945     }
04946     // if device is explictly listed and we can't register it, then
04947     // exit unless the user has specified that the device is removable
04948     else if (!scanning) {
04949       if (cfg.removable || quit==2)
04950         PrintOut(LOG_INFO, "Device %s not available\n", cfg.name.c_str());
04951       else {
04952         PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", cfg.name.c_str());
04953         EXIT(EXIT_BADDEV);
04954       }
04955     }
04956   }
04957 
04958   init_disable_standby_check(configs);
04959 }
04960 
04961 
04962 // Main program without exception handling
04963 static int main_worker(int argc, char **argv)
04964 {
04965   // Initialize interface
04966   smart_interface::init();
04967   if (!smi())
04968     return 1;
04969 
04970   // is it our first pass through?
04971   bool firstpass = true;
04972 
04973   // next time to wake up
04974   time_t wakeuptime = 0;
04975 
04976   // parse input and print header and usage info if needed
04977   ParseOpts(argc,argv);
04978   
04979   // Configuration for each device
04980   dev_config_vector configs;
04981   // Device states
04982   dev_state_vector states;
04983   // Devices to monitor
04984   smart_device_list devices;
04985 
04986   bool write_states_always = true;
04987 
04988 #ifdef HAVE_LIBCAP_NG
04989   // Drop capabilities
04990   if (enable_capabilities) {
04991     capng_clear(CAPNG_SELECT_BOTH);
04992     capng_updatev(CAPNG_ADD, (capng_type_t)(CAPNG_EFFECTIVE|CAPNG_PERMITTED),
04993                   CAP_SYS_ADMIN, CAP_MKNOD, CAP_SYS_RAWIO, -1);
04994     capng_apply(CAPNG_SELECT_BOTH);
04995   }
04996 #endif
04997 
04998   // the main loop of the code
04999   for (;;) {
05000 
05001     // are we exiting from a signal?
05002     if (caughtsigEXIT) {
05003       // are we exiting with SIGTERM?
05004       int isterm=(caughtsigEXIT==SIGTERM);
05005       int isquit=(caughtsigEXIT==SIGQUIT);
05006       int isok=debugmode?isterm || isquit:isterm;
05007       
05008       PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n",
05009                caughtsigEXIT, strsignal(caughtsigEXIT));
05010 
05011       if (!isok)
05012         return EXIT_SIGNAL;
05013 
05014       // Write state files
05015       if (!state_path_prefix.empty())
05016         write_all_dev_states(configs, states);
05017 
05018       return 0;
05019     }
05020 
05021     // Should we (re)read the config file?
05022     if (firstpass || caughtsigHUP){
05023       if (!firstpass) {
05024         // Write state files
05025         if (!state_path_prefix.empty())
05026           write_all_dev_states(configs, states);
05027 
05028         PrintOut(LOG_INFO,
05029                  caughtsigHUP==1?
05030                  "Signal HUP - rereading configuration file %s\n":
05031                  "\a\nSignal INT - rereading configuration file %s (" SIGQUIT_KEYNAME " quits)\n\n",
05032                  configfile);
05033       }
05034 
05035       {
05036         dev_config_vector conf_entries; // Entries read from smartd.conf
05037         smart_device_list scanned_devs; // Devices found during scan
05038         // (re)reads config file, makes >=0 entries
05039         int entries = ReadOrMakeConfigEntries(conf_entries, scanned_devs);
05040 
05041         if (entries>=0) {
05042           // checks devices, then moves onto ata/scsi list or deallocates.
05043           RegisterDevices(conf_entries, scanned_devs, configs, states, devices);
05044           if (!(configs.size() == devices.size() && configs.size() == states.size()))
05045             throw std::logic_error("Invalid result from RegisterDevices");
05046         }
05047         else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) {
05048           // user has asked to continue on error in configuration file
05049           if (!firstpass)
05050             PrintOut(LOG_INFO,"Reusing previous configuration\n");
05051         }
05052         else {
05053           // exit with configuration file error status
05054           return (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF);
05055         }
05056       }
05057 
05058       // Log number of devices we are monitoring...
05059       if (devices.size() > 0 || quit==2 || (quit==1 && !firstpass)) {
05060         int numata = 0;
05061         for (unsigned i = 0; i < devices.size(); i++) {
05062           if (devices.at(i)->is_ata())
05063             numata++;
05064         }
05065         PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
05066                  numata, devices.size() - numata);
05067       }
05068       else {
05069         PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
05070         return EXIT_NODEV;
05071       }
05072 
05073       if (quit==4) {
05074         // user has asked to print test schedule
05075         PrintTestSchedule(configs, states, devices);
05076         return 0;
05077       }
05078 
05079 #ifdef HAVE_LIBCAP_NG
05080       if (enable_capabilities) {
05081         for (unsigned i = 0; i < configs.size(); i++) {
05082           if (!configs[i].emailaddress.empty() || !configs[i].emailcmdline.empty()) {
05083             PrintOut(LOG_WARNING, "Mail can't be enabled together with --capabilities. All mail will be suppressed.\n");
05084             break;
05085           }
05086         }
05087       }
05088 #endif
05089 
05090       // reset signal
05091       caughtsigHUP=0;
05092 
05093       // Always write state files after (re)configuration
05094       write_states_always = true;
05095     }
05096 
05097     // check all devices once,
05098     // self tests are not started in first pass unless '-q onecheck' is specified
05099     CheckDevicesOnce(configs, states, devices, firstpass, (!firstpass || quit==3));
05100 
05101      // Write state files
05102     if (!state_path_prefix.empty())
05103       write_all_dev_states(configs, states, write_states_always);
05104     write_states_always = false;
05105 
05106     // Write attribute logs
05107     if (!attrlog_path_prefix.empty())
05108       write_all_dev_attrlogs(configs, states);
05109 
05110     // user has asked us to exit after first check
05111     if (quit==3) {
05112       PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n"
05113                "smartd is exiting (exit status 0)\n");
05114       return 0;
05115     }
05116     
05117     // fork into background if needed
05118     if (firstpass && !debugmode) {
05119       DaemonInit();
05120     }
05121 
05122     // set exit and signal handlers, write PID file, set wake-up time
05123     if (firstpass){
05124       Initialize(&wakeuptime);
05125       firstpass = false;
05126     }
05127     
05128     // sleep until next check time, or a signal arrives
05129     wakeuptime = dosleep(wakeuptime, write_states_always);
05130   }
05131 }
05132 
05133 
05134 #ifndef _WIN32
05135 // Main program
05136 int main(int argc, char **argv)
05137 #else
05138 // Windows: internal main function started direct or by service control manager
05139 static int smartd_main(int argc, char **argv)
05140 #endif
05141 {
05142   int status;
05143   try {
05144     // Do the real work ...
05145     status = main_worker(argc, argv);
05146   }
05147   catch (int ex) {
05148     // EXIT(status) arrives here
05149     status = ex;
05150   }
05151   catch (const std::bad_alloc & /*ex*/) {
05152     // Memory allocation failed (also thrown by std::operator new)
05153     PrintOut(LOG_CRIT, "Smartd: Out of memory\n");
05154     status = EXIT_NOMEM;
05155   }
05156   catch (const std::exception & ex) {
05157     // Other fatal errors
05158     PrintOut(LOG_CRIT, "Smartd: Exception: %s\n", ex.what());
05159     status = EXIT_BADCODE;
05160   }
05161 
05162   if (is_initialized)
05163     status = Goodbye(status);
05164 
05165 #ifdef _WIN32
05166   daemon_winsvc_exitcode = status;
05167 #endif
05168   return status;
05169 }
05170 
05171 
05172 #ifdef _WIN32
05173 // Main function for Windows
05174 int main(int argc, char **argv){
05175   // Options for smartd windows service
05176   static const daemon_winsvc_options svc_opts = {
05177     "--service", // cmd_opt
05178     "smartd", "SmartD Service", // servicename, displayname
05179     // description
05180     "Controls and monitors storage devices using the Self-Monitoring, "
05181     "Analysis and Reporting Technology System (S.M.A.R.T.) "
05182     "built into ATA and SCSI Hard Drives. "
05183     PACKAGE_HOMEPAGE
05184   };
05185   // daemon_main() handles daemon and service specific commands
05186   // and starts smartd_main() direct, from a new process,
05187   // or via service control manager
05188   return daemon_main("smartd", &svc_opts , smartd_main, argc, argv);
05189 }
05190 #endif