Annotation of embedaddon/smartmontools/smartd.cpp, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Home page of code is: http://smartmontools.sourceforge.net
        !             3:  *
        !             4:  * Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net>
        !             5:  * Copyright (C) 2000    Michael Cornwell <cornwell@acm.org>
        !             6:  * Copyright (C) 2008    Oliver Bock <brevilo@users.sourceforge.net>
        !             7:  * Copyright (C) 2008-11 Christian Franke <smartmontools-support@lists.sourceforge.net>
        !             8:  *
        !             9:  * This program is free software; you can redistribute it and/or modify
        !            10:  * it under the terms of the GNU General Public License as published by
        !            11:  * the Free Software Foundation; either version 2, or (at your option)
        !            12:  * any later version.
        !            13:  *
        !            14:  * You should have received a copy of the GNU General Public License
        !            15:  * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
        !            16:  *
        !            17:  * This code was originally developed as a Senior Thesis by Michael Cornwell
        !            18:  * at the Concurrent Systems Laboratory (now part of the Storage Systems
        !            19:  * Research Center), Jack Baskin School of Engineering, University of
        !            20:  * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
        !            21:  *
        !            22:  */
        !            23: 
        !            24: #ifndef _GNU_SOURCE
        !            25: // TODO: Why is this define necessary?
        !            26: #define _GNU_SOURCE
        !            27: #endif
        !            28: 
        !            29: // unconditionally included files
        !            30: #include <stdio.h>
        !            31: #include <sys/types.h>
        !            32: #include <sys/stat.h>   // umask
        !            33: #include <signal.h>
        !            34: #include <fcntl.h>
        !            35: #include <string.h>
        !            36: #include <syslog.h>
        !            37: #include <stdarg.h>
        !            38: #include <stdlib.h>
        !            39: #include <errno.h>
        !            40: #include <time.h>
        !            41: #include <limits.h>
        !            42: #include <getopt.h>
        !            43: 
        !            44: #include <stdexcept>
        !            45: #include <string>
        !            46: #include <vector>
        !            47: #include <algorithm> // std::replace()
        !            48: 
        !            49: // see which system files to conditionally include
        !            50: #include "config.h"
        !            51: 
        !            52: // conditionally included files
        !            53: #ifndef _WIN32
        !            54: #include <sys/wait.h>
        !            55: #endif
        !            56: #ifdef HAVE_UNISTD_H
        !            57: #include <unistd.h>
        !            58: #endif
        !            59: #ifdef HAVE_NETDB_H
        !            60: #include <netdb.h>
        !            61: #endif
        !            62: 
        !            63: #ifdef _WIN32
        !            64: #ifdef _MSC_VER
        !            65: #pragma warning(disable:4761) // "conversion supplied"
        !            66: typedef unsigned short mode_t;
        !            67: typedef int pid_t;
        !            68: #endif
        !            69: #include <io.h> // umask()
        !            70: #include <process.h> // getpid()
        !            71: #endif // _WIN32
        !            72: 
        !            73: #ifdef __CYGWIN__
        !            74: #include <io.h> // setmode()
        !            75: #endif // __CYGWIN__
        !            76: 
        !            77: #ifdef HAVE_LIBCAP_NG
        !            78: #include <cap-ng.h>
        !            79: #endif // LIBCAP_NG
        !            80: 
        !            81: // locally included files
        !            82: #include "int64.h"
        !            83: #include "atacmds.h"
        !            84: #include "dev_interface.h"
        !            85: #include "knowndrives.h"
        !            86: #include "scsicmds.h"
        !            87: #include "utility.h"
        !            88: 
        !            89: // This is for solaris, where signal() resets the handler to SIG_DFL
        !            90: // after the first signal is caught.
        !            91: #ifdef HAVE_SIGSET
        !            92: #define SIGNALFN sigset
        !            93: #else
        !            94: #define SIGNALFN signal
        !            95: #endif
        !            96: 
        !            97: #ifdef _WIN32
        !            98: #include "hostname_win32.h" // gethost/domainname()
        !            99: #define HAVE_GETHOSTNAME   1
        !           100: #define HAVE_GETDOMAINNAME 1
        !           101: // fork()/signal()/initd simulation for native Windows
        !           102: #include "daemon_win32.h" // daemon_main/detach/signal()
        !           103: #undef SIGNALFN
        !           104: #define SIGNALFN  daemon_signal
        !           105: #define strsignal daemon_strsignal
        !           106: #define sleep     daemon_sleep
        !           107: // SIGQUIT does not exist, CONTROL-Break signals SIGBREAK.
        !           108: #define SIGQUIT SIGBREAK
        !           109: #define SIGQUIT_KEYNAME "CONTROL-Break"
        !           110: #else  // _WIN32
        !           111: #ifdef __CYGWIN__
        !           112: // 2x CONTROL-C simulates missing SIGQUIT via keyboard
        !           113: #define SIGQUIT_KEYNAME "2x CONTROL-C"
        !           114: #else // __CYGWIN__
        !           115: #define SIGQUIT_KEYNAME "CONTROL-\\"
        !           116: #endif // __CYGWIN__
        !           117: #endif // _WIN32
        !           118: 
        !           119: #if defined (__SVR4) && defined (__sun)
        !           120: extern "C" int getdomainname(char *, int); // no declaration in header files!
        !           121: #endif
        !           122: 
        !           123: #define ARGUSED(x) ((void)(x))
        !           124: 
        !           125: const char * smartd_cpp_cvsid = "$Id: smartd.cpp 3451 2011-10-15 14:27:08Z chrfranke $"
        !           126:   CONFIG_H_CVSID;
        !           127: 
        !           128: // smartd exit codes
        !           129: #define EXIT_BADCMD    1   // command line did not parse
        !           130: #define EXIT_BADCONF   2   // syntax error in config file
        !           131: #define EXIT_STARTUP   3   // problem forking daemon
        !           132: #define EXIT_PID       4   // problem creating pid file
        !           133: #define EXIT_NOCONF    5   // config file does not exist
        !           134: #define EXIT_READCONF  6   // config file exists but cannot be read
        !           135: 
        !           136: #define EXIT_NOMEM     8   // out of memory
        !           137: #define EXIT_BADCODE   10  // internal error - should NEVER happen
        !           138: 
        !           139: #define EXIT_BADDEV    16  // we can't monitor this device
        !           140: #define EXIT_NODEV     17  // no devices to monitor
        !           141: 
        !           142: #define EXIT_SIGNAL    254 // abort on signal
        !           143: 
        !           144: 
        !           145: // command-line: 1=debug mode, 2=print presets
        !           146: static unsigned char debugmode = 0;
        !           147: 
        !           148: // command-line: how long to sleep between checks
        !           149: #define CHECKTIME 1800
        !           150: static int checktime=CHECKTIME;
        !           151: 
        !           152: // command-line: name of PID file (empty for no pid file)
        !           153: static std::string pid_file;
        !           154: 
        !           155: // command-line: path prefix of persistent state file, empty if no persistence.
        !           156: static std::string state_path_prefix
        !           157: #ifdef SMARTMONTOOLS_SAVESTATES
        !           158:           = SMARTMONTOOLS_SAVESTATES
        !           159: #endif
        !           160:                                     ;
        !           161: 
        !           162: // command-line: path prefix of attribute log file, empty if no logs.
        !           163: static std::string attrlog_path_prefix
        !           164: #ifdef SMARTMONTOOLS_ATTRIBUTELOG
        !           165:           = SMARTMONTOOLS_ATTRIBUTELOG
        !           166: #endif
        !           167:                                     ;
        !           168: 
        !           169: // configuration file name
        !           170: static const char * configfile;
        !           171: // configuration file "name" if read from stdin
        !           172: static const char * const configfile_stdin = "<stdin>";
        !           173: // path of alternate configuration file
        !           174: static std::string configfile_alt;
        !           175: 
        !           176: // command-line: when should we exit?
        !           177: static int quit=0;
        !           178: 
        !           179: // command-line; this is the default syslog(3) log facility to use.
        !           180: static int facility=LOG_DAEMON;
        !           181: 
        !           182: #ifndef _WIN32
        !           183: // command-line: fork into background?
        !           184: static bool do_fork=true;
        !           185: #endif
        !           186: 
        !           187: #ifdef HAVE_LIBCAP_NG
        !           188: // command-line: enable capabilities?
        !           189: static bool enable_capabilities = false;
        !           190: #endif
        !           191: 
        !           192: #if defined(_WIN32) || defined(__CYGWIN__)
        !           193: // TODO: This smartctl only variable is also used in os_win32.cpp
        !           194: unsigned char failuretest_permissive = 0;
        !           195: #endif
        !           196: 
        !           197: // set to one if we catch a USR1 (check devices now)
        !           198: static volatile int caughtsigUSR1=0;
        !           199: 
        !           200: #ifdef _WIN32
        !           201: // set to one if we catch a USR2 (toggle debug mode)
        !           202: static volatile int caughtsigUSR2=0;
        !           203: #endif
        !           204: 
        !           205: // set to one if we catch a HUP (reload config file). In debug mode,
        !           206: // set to two, if we catch INT (also reload config file).
        !           207: static volatile int caughtsigHUP=0;
        !           208: 
        !           209: // set to signal value if we catch INT, QUIT, or TERM
        !           210: static volatile int caughtsigEXIT=0;
        !           211: 
        !           212: // This function prints either to stdout or to the syslog as needed.
        !           213: static void PrintOut(int priority, const char *fmt, ...)
        !           214:                      __attribute__ ((format(printf, 2, 3)));
        !           215: 
        !           216: // Attribute monitoring flags.
        !           217: // See monitor_attr_flags below.
        !           218: enum {
        !           219:   MONITOR_IGN_FAILUSE = 0x01,
        !           220:   MONITOR_IGNORE      = 0x02,
        !           221:   MONITOR_RAW_PRINT   = 0x04,
        !           222:   MONITOR_RAW         = 0x08,
        !           223:   MONITOR_AS_CRIT     = 0x10,
        !           224:   MONITOR_RAW_AS_CRIT = 0x20,
        !           225: };
        !           226: 
        !           227: // Array of flags for each attribute.
        !           228: class attribute_flags
        !           229: {
        !           230: public:
        !           231:   attribute_flags()
        !           232:     { memset(m_flags, 0, sizeof(m_flags)); }
        !           233: 
        !           234:   bool is_set(int id, unsigned char flag) const
        !           235:     { return (0 < id && id < (int)sizeof(m_flags) && (m_flags[id] & flag)); }
        !           236: 
        !           237:   void set(int id, unsigned char flags)
        !           238:     {
        !           239:       if (0 < id && id < (int)sizeof(m_flags))
        !           240:         m_flags[id] |= flags;
        !           241:     }
        !           242: 
        !           243: private:
        !           244:   unsigned char m_flags[256];
        !           245: };
        !           246: 
        !           247: 
        !           248: /// Configuration data for a device. Read from smartd.conf.
        !           249: /// Supports copy & assignment and is compatible with STL containers.
        !           250: struct dev_config
        !           251: {
        !           252:   int lineno;                             // Line number of entry in file
        !           253:   std::string name;                       // Device name (with optional extra info)
        !           254:   std::string dev_name;                   // Device name (plain, for SMARTD_DEVICE variable)
        !           255:   std::string dev_type;                   // Device type argument from -d directive, empty if none
        !           256:   std::string state_file;                 // Path of the persistent state file, empty if none
        !           257:   std::string attrlog_file;               // Path of the persistent attrlog file, empty if none
        !           258:   bool smartcheck;                        // Check SMART status
        !           259:   bool usagefailed;                       // Check for failed Usage Attributes
        !           260:   bool prefail;                           // Track changes in Prefail Attributes
        !           261:   bool usage;                             // Track changes in Usage Attributes
        !           262:   bool selftest;                          // Monitor number of selftest errors
        !           263:   bool errorlog;                          // Monitor number of ATA errors
        !           264:   bool xerrorlog;                         // Monitor number of ATA errors (Extended Comprehensive error log)
        !           265:   bool offlinests;                        // Monitor changes in offline data collection status
        !           266:   bool selfteststs;                       // Monitor changes in self-test execution status
        !           267:   bool permissive;                        // Ignore failed SMART commands
        !           268:   char autosave;                          // 1=disable, 2=enable Autosave Attributes
        !           269:   char autoofflinetest;                   // 1=disable, 2=enable Auto Offline Test
        !           270:   unsigned char fix_firmwarebug;          // FIX_*, see atacmds.h
        !           271:   bool ignorepresets;                     // Ignore database of -v options
        !           272:   bool showpresets;                       // Show database entry for this device
        !           273:   bool removable;                         // Device may disappear (not be present)
        !           274:   char powermode;                         // skip check, if disk in idle or standby mode
        !           275:   bool powerquiet;                        // skip powermode 'skipping checks' message
        !           276:   int powerskipmax;                       // how many times can be check skipped
        !           277:   unsigned char tempdiff;                 // Track Temperature changes >= this limit
        !           278:   unsigned char tempinfo, tempcrit;       // Track Temperatures >= these limits as LOG_INFO, LOG_CRIT+mail
        !           279:   regular_expression test_regex;          // Regex for scheduled testing
        !           280: 
        !           281:   // Configuration of email warning messages
        !           282:   std::string emailcmdline;               // script to execute, empty if no messages
        !           283:   std::string emailaddress;               // email address, or empty
        !           284:   unsigned char emailfreq;                // Emails once (1) daily (2) diminishing (3)
        !           285:   bool emailtest;                         // Send test email?
        !           286: 
        !           287:   // ATA ONLY
        !           288:   bool sct_erc_set;                       // set SCT ERC to:
        !           289:   unsigned short sct_erc_readtime;        // ERC read time (deciseconds)
        !           290:   unsigned short sct_erc_writetime;       // ERC write time (deciseconds)
        !           291: 
        !           292:   unsigned char curr_pending_id;          // ID of current pending sector count, 0 if none
        !           293:   unsigned char offl_pending_id;          // ID of offline uncorrectable sector count, 0 if none
        !           294:   bool curr_pending_incr, offl_pending_incr; // True if current/offline pending values increase
        !           295:   bool curr_pending_set,  offl_pending_set;  // True if '-C', '-U' set in smartd.conf
        !           296: 
        !           297:   attribute_flags monitor_attr_flags;     // MONITOR_* flags for each attribute
        !           298: 
        !           299:   ata_vendor_attr_defs attribute_defs;    // -v options
        !           300: 
        !           301:   dev_config();
        !           302: };
        !           303: 
        !           304: dev_config::dev_config()
        !           305: : lineno(0),
        !           306:   smartcheck(false),
        !           307:   usagefailed(false),
        !           308:   prefail(false),
        !           309:   usage(false),
        !           310:   selftest(false),
        !           311:   errorlog(false),
        !           312:   xerrorlog(false),
        !           313:   offlinests(false),
        !           314:   selfteststs(false),
        !           315:   permissive(false),
        !           316:   autosave(0),
        !           317:   autoofflinetest(0),
        !           318:   fix_firmwarebug(FIX_NOTSPECIFIED),
        !           319:   ignorepresets(false),
        !           320:   showpresets(false),
        !           321:   removable(false),
        !           322:   powermode(0),
        !           323:   powerquiet(false),
        !           324:   powerskipmax(0),
        !           325:   tempdiff(0),
        !           326:   tempinfo(0), tempcrit(0),
        !           327:   emailfreq(0),
        !           328:   emailtest(false),
        !           329:   sct_erc_set(false),
        !           330:   sct_erc_readtime(0), sct_erc_writetime(0),
        !           331:   curr_pending_id(0), offl_pending_id(0),
        !           332:   curr_pending_incr(false), offl_pending_incr(false),
        !           333:   curr_pending_set(false),  offl_pending_set(false)
        !           334: {
        !           335: }
        !           336: 
        !           337: 
        !           338: // Number of allowed mail message types
        !           339: static const int SMARTD_NMAIL = 13;
        !           340: // Type for '-M test' mails (state not persistent)
        !           341: static const int MAILTYPE_TEST = 0;
        !           342: // TODO: Add const or enum for all mail types.
        !           343: 
        !           344: struct mailinfo {
        !           345:   int logged;// number of times an email has been sent
        !           346:   time_t firstsent;// time first email was sent, as defined by time(2)
        !           347:   time_t lastsent; // time last email was sent, as defined by time(2)
        !           348: 
        !           349:   mailinfo()
        !           350:     : logged(0), firstsent(0), lastsent(0) { }
        !           351: };
        !           352: 
        !           353: /// Persistent state data for a device.
        !           354: struct persistent_dev_state
        !           355: {
        !           356:   unsigned char tempmin, tempmax;         // Min/Max Temperatures
        !           357: 
        !           358:   unsigned char selflogcount;             // total number of self-test errors
        !           359:   unsigned short selfloghour;             // lifetime hours of last self-test error
        !           360: 
        !           361:   time_t scheduled_test_next_check;       // Time of next check for scheduled self-tests
        !           362: 
        !           363:   uint64_t selective_test_last_start;     // Start LBA of last scheduled selective self-test
        !           364:   uint64_t selective_test_last_end;       // End LBA of last scheduled selective self-test
        !           365: 
        !           366:   mailinfo maillog[SMARTD_NMAIL];         // log info on when mail sent
        !           367: 
        !           368:   // ATA ONLY
        !           369:   int ataerrorcount;                      // Total number of ATA errors
        !           370: 
        !           371:   // Persistent part of ata_smart_values:
        !           372:   struct ata_attribute {
        !           373:     unsigned char id;
        !           374:     unsigned char val;
        !           375:     unsigned char worst; // Byte needed for 'raw64' attribute only.
        !           376:     uint64_t raw;
        !           377:     unsigned char resvd;
        !           378: 
        !           379:     ata_attribute() : id(0), val(0), worst(0), raw(0), resvd(0) { }
        !           380:   };
        !           381:   ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES];
        !           382: 
        !           383:   persistent_dev_state();
        !           384: };
        !           385: 
        !           386: persistent_dev_state::persistent_dev_state()
        !           387: : tempmin(0), tempmax(0),
        !           388:   selflogcount(0),
        !           389:   selfloghour(0),
        !           390:   scheduled_test_next_check(0),
        !           391:   selective_test_last_start(0),
        !           392:   selective_test_last_end(0),
        !           393:   ataerrorcount(0)
        !           394: {
        !           395: }
        !           396: 
        !           397: /// Non-persistent state data for a device.
        !           398: struct temp_dev_state
        !           399: {
        !           400:   bool must_write;                        // true if persistent part should be written
        !           401: 
        !           402:   bool not_cap_offline;                   // true == not capable of offline testing
        !           403:   bool not_cap_conveyance;
        !           404:   bool not_cap_short;
        !           405:   bool not_cap_long;
        !           406:   bool not_cap_selective;
        !           407: 
        !           408:   unsigned char temperature;              // last recorded Temperature (in Celsius)
        !           409:   time_t tempmin_delay;                   // time where Min Temperature tracking will start
        !           410: 
        !           411:   bool powermodefail;                     // true if power mode check failed
        !           412:   int powerskipcnt;                       // Number of checks skipped due to idle or standby mode
        !           413: 
        !           414:   // SCSI ONLY
        !           415:   unsigned char SmartPageSupported;       // has log sense IE page (0x2f)
        !           416:   unsigned char TempPageSupported;        // has log sense temperature page (0xd)
        !           417:   unsigned char SuppressReport;           // minimize nuisance reports
        !           418:   unsigned char modese_len;               // mode sense/select cmd len: 0 (don't
        !           419:                                           // know yet) 6 or 10
        !           420: 
        !           421:   // ATA ONLY
        !           422:   uint64_t num_sectors;                   // Number of sectors
        !           423:   ata_smart_values smartval;              // SMART data
        !           424:   ata_smart_thresholds_pvt smartthres;    // SMART thresholds
        !           425: 
        !           426:   temp_dev_state();
        !           427: };
        !           428: 
        !           429: temp_dev_state::temp_dev_state()
        !           430: : must_write(false),
        !           431:   not_cap_offline(false),
        !           432:   not_cap_conveyance(false),
        !           433:   not_cap_short(false),
        !           434:   not_cap_long(false),
        !           435:   not_cap_selective(false),
        !           436:   temperature(0),
        !           437:   tempmin_delay(0),
        !           438:   powermodefail(false),
        !           439:   powerskipcnt(0),
        !           440:   SmartPageSupported(false),
        !           441:   TempPageSupported(false),
        !           442:   SuppressReport(false),
        !           443:   modese_len(0),
        !           444:   num_sectors(0)
        !           445: {
        !           446:   memset(&smartval, 0, sizeof(smartval));
        !           447:   memset(&smartthres, 0, sizeof(smartthres));
        !           448: }
        !           449: 
        !           450: /// Runtime state data for a device.
        !           451: struct dev_state
        !           452: : public persistent_dev_state,
        !           453:   public temp_dev_state
        !           454: {
        !           455:   void update_persistent_state();
        !           456:   void update_temp_state();
        !           457: };
        !           458: 
        !           459: /// Container for configuration info for each device.
        !           460: typedef std::vector<dev_config> dev_config_vector;
        !           461: 
        !           462: /// Container for state info for each device.
        !           463: typedef std::vector<dev_state> dev_state_vector;
        !           464: 
        !           465: // Copy ATA attributes to persistent state.
        !           466: void dev_state::update_persistent_state()
        !           467: {
        !           468:   for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
        !           469:     const ata_smart_attribute & ta = smartval.vendor_attributes[i];
        !           470:     ata_attribute & pa = ata_attributes[i];
        !           471:     pa.id = ta.id;
        !           472:     if (ta.id == 0) {
        !           473:       pa.val = pa.worst = 0; pa.raw = 0;
        !           474:       continue;
        !           475:     }
        !           476:     pa.val = ta.current;
        !           477:     pa.worst = ta.worst;
        !           478:     pa.raw =            ta.raw[0]
        !           479:            | (          ta.raw[1] <<  8)
        !           480:            | (          ta.raw[2] << 16)
        !           481:            | ((uint64_t)ta.raw[3] << 24)
        !           482:            | ((uint64_t)ta.raw[4] << 32)
        !           483:            | ((uint64_t)ta.raw[5] << 40);
        !           484:     pa.resvd = ta.reserv;
        !           485:   }
        !           486: }
        !           487: 
        !           488: // Copy ATA from persistent to temp state.
        !           489: void dev_state::update_temp_state()
        !           490: {
        !           491:   for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
        !           492:     const ata_attribute & pa = ata_attributes[i];
        !           493:     ata_smart_attribute & ta = smartval.vendor_attributes[i];
        !           494:     ta.id = pa.id;
        !           495:     if (pa.id == 0) {
        !           496:       ta.current = ta.worst = 0;
        !           497:       memset(ta.raw, 0, sizeof(ta.raw));
        !           498:       continue;
        !           499:     }
        !           500:     ta.current = pa.val;
        !           501:     ta.worst = pa.worst;
        !           502:     ta.raw[0] = (unsigned char) pa.raw;
        !           503:     ta.raw[1] = (unsigned char)(pa.raw >>  8);
        !           504:     ta.raw[2] = (unsigned char)(pa.raw >> 16);
        !           505:     ta.raw[3] = (unsigned char)(pa.raw >> 24);
        !           506:     ta.raw[4] = (unsigned char)(pa.raw >> 32);
        !           507:     ta.raw[5] = (unsigned char)(pa.raw >> 40);
        !           508:     ta.reserv = pa.resvd;
        !           509:   }
        !           510: }
        !           511: 
        !           512: // Parse a line from a state file.
        !           513: static bool parse_dev_state_line(const char * line, persistent_dev_state & state)
        !           514: {
        !           515:   static const regular_expression regex(
        !           516:     "^ *"
        !           517:      "((temperature-min)" // (1 (2)
        !           518:      "|(temperature-max)" // (3)
        !           519:      "|(self-test-errors)" // (4)
        !           520:      "|(self-test-last-err-hour)" // (5)
        !           521:      "|(scheduled-test-next-check)" // (6)
        !           522:      "|(selective-test-last-start)" // (7)
        !           523:      "|(selective-test-last-end)" // (8)
        !           524:      "|(ata-error-count)"  // (9)
        !           525:      "|(mail\\.([0-9]+)\\." // (10 (11)
        !           526:        "((count)" // (12 (13)
        !           527:        "|(first-sent-time)" // (14)
        !           528:        "|(last-sent-time)" // (15)
        !           529:        ")" // 12)
        !           530:       ")" // 10)
        !           531:      "|(ata-smart-attribute\\.([0-9]+)\\." // (16 (17)
        !           532:        "((id)" // (18 (19)
        !           533:        "|(val)" // (20)
        !           534:        "|(worst)" // (21)
        !           535:        "|(raw)" // (22)
        !           536:        "|(resvd)" // (23)
        !           537:        ")" // 18)
        !           538:       ")" // 16)
        !           539:      ")" // 1)
        !           540:      " *= *([0-9]+)[ \n]*$", // (24)
        !           541:     REG_EXTENDED
        !           542:   );
        !           543: 
        !           544:   const int nmatch = 1+24;
        !           545:   regmatch_t match[nmatch];
        !           546:   if (!regex.execute(line, nmatch, match))
        !           547:     return false;
        !           548:   if (match[nmatch-1].rm_so < 0)
        !           549:     return false;
        !           550: 
        !           551:   uint64_t val = strtoull(line + match[nmatch-1].rm_so, (char **)0, 10);
        !           552: 
        !           553:   int m = 1;
        !           554:   if (match[++m].rm_so >= 0)
        !           555:     state.tempmin = (unsigned char)val;
        !           556:   else if (match[++m].rm_so >= 0)
        !           557:     state.tempmax = (unsigned char)val;
        !           558:   else if (match[++m].rm_so >= 0)
        !           559:     state.selflogcount = (unsigned char)val;
        !           560:   else if (match[++m].rm_so >= 0)
        !           561:     state.selfloghour = (unsigned short)val;
        !           562:   else if (match[++m].rm_so >= 0)
        !           563:     state.scheduled_test_next_check = (time_t)val;
        !           564:   else if (match[++m].rm_so >= 0)
        !           565:     state.selective_test_last_start = val;
        !           566:   else if (match[++m].rm_so >= 0)
        !           567:     state.selective_test_last_end = val;
        !           568:   else if (match[++m].rm_so >= 0)
        !           569:     state.ataerrorcount = (int)val;
        !           570:   else if (match[m+=2].rm_so >= 0) {
        !           571:     int i = atoi(line+match[m].rm_so);
        !           572:     if (!(0 <= i && i < SMARTD_NMAIL))
        !           573:       return false;
        !           574:     if (i == MAILTYPE_TEST) // Don't suppress test mails
        !           575:       return true;
        !           576:     if (match[m+=2].rm_so >= 0)
        !           577:       state.maillog[i].logged = (int)val;
        !           578:     else if (match[++m].rm_so >= 0)
        !           579:       state.maillog[i].firstsent = (time_t)val;
        !           580:     else if (match[++m].rm_so >= 0)
        !           581:       state.maillog[i].lastsent = (time_t)val;
        !           582:     else
        !           583:       return false;
        !           584:   }
        !           585:   else if (match[m+=5+1].rm_so >= 0) {
        !           586:     int i = atoi(line+match[m].rm_so);
        !           587:     if (!(0 <= i && i < NUMBER_ATA_SMART_ATTRIBUTES))
        !           588:       return false;
        !           589:     if (match[m+=2].rm_so >= 0)
        !           590:       state.ata_attributes[i].id = (unsigned char)val;
        !           591:     else if (match[++m].rm_so >= 0)
        !           592:       state.ata_attributes[i].val = (unsigned char)val;
        !           593:     else if (match[++m].rm_so >= 0)
        !           594:       state.ata_attributes[i].worst = (unsigned char)val;
        !           595:     else if (match[++m].rm_so >= 0)
        !           596:       state.ata_attributes[i].raw = val;
        !           597:     else if (match[++m].rm_so >= 0)
        !           598:       state.ata_attributes[i].resvd = (unsigned char)val;
        !           599:     else
        !           600:       return false;
        !           601:   }
        !           602:   else
        !           603:     return false;
        !           604:   return true;
        !           605: }
        !           606: 
        !           607: // Read a state file.
        !           608: static bool read_dev_state(const char * path, persistent_dev_state & state)
        !           609: {
        !           610:   stdio_file f(path, "r");
        !           611:   if (!f) {
        !           612:     if (errno != ENOENT)
        !           613:       pout("Cannot read state file \"%s\"\n", path);
        !           614:     return false;
        !           615:   }
        !           616: #ifdef __CYGWIN__
        !           617:   setmode(fileno(f), O_TEXT); // Allow files with \r\n
        !           618: #endif
        !           619: 
        !           620:   persistent_dev_state new_state;
        !           621:   int good = 0, bad = 0;
        !           622:   char line[256];
        !           623:   while (fgets(line, sizeof(line), f)) {
        !           624:     const char * s = line + strspn(line, " \t");
        !           625:     if (!*s || *s == '#')
        !           626:       continue;
        !           627:     if (!parse_dev_state_line(line, new_state))
        !           628:       bad++;
        !           629:     else
        !           630:       good++;
        !           631:   }
        !           632: 
        !           633:   if (bad) {
        !           634:     if (!good) {
        !           635:       pout("%s: format error\n", path);
        !           636:       return false;
        !           637:     }
        !           638:     pout("%s: %d invalid line(s) ignored\n", path, bad);
        !           639:   }
        !           640: 
        !           641:   // This sets the values missing in the file to 0.
        !           642:   state = new_state;
        !           643:   return true;
        !           644: }
        !           645: 
        !           646: static void write_dev_state_line(FILE * f, const char * name, uint64_t val)
        !           647: {
        !           648:   if (val)
        !           649:     fprintf(f, "%s = %"PRIu64"\n", name, val);
        !           650: }
        !           651: 
        !           652: static void write_dev_state_line(FILE * f, const char * name1, int id, const char * name2, uint64_t val)
        !           653: {
        !           654:   if (val)
        !           655:     fprintf(f, "%s.%d.%s = %"PRIu64"\n", name1, id, name2, val);
        !           656: }
        !           657: 
        !           658: // Write a state file
        !           659: static bool write_dev_state(const char * path, const persistent_dev_state & state)
        !           660: {
        !           661:   // Rename old "file" to "file~"
        !           662:   std::string pathbak = path; pathbak += '~';
        !           663:   unlink(pathbak.c_str());
        !           664:   rename(path, pathbak.c_str());
        !           665: 
        !           666:   stdio_file f(path, "w");
        !           667:   if (!f) {
        !           668:     pout("Cannot create state file \"%s\"\n", path);
        !           669:     return false;
        !           670:   }
        !           671: 
        !           672:   fprintf(f, "# smartd state file\n");
        !           673:   write_dev_state_line(f, "temperature-min", state.tempmin);
        !           674:   write_dev_state_line(f, "temperature-max", state.tempmax);
        !           675:   write_dev_state_line(f, "self-test-errors", state.selflogcount);
        !           676:   write_dev_state_line(f, "self-test-last-err-hour", state.selfloghour);
        !           677:   write_dev_state_line(f, "scheduled-test-next-check", state.scheduled_test_next_check);
        !           678:   write_dev_state_line(f, "selective-test-last-start", state.selective_test_last_start);
        !           679:   write_dev_state_line(f, "selective-test-last-end", state.selective_test_last_end);
        !           680: 
        !           681:   int i;
        !           682:   for (i = 0; i < SMARTD_NMAIL; i++) {
        !           683:     if (i == MAILTYPE_TEST) // Don't suppress test mails
        !           684:       continue;
        !           685:     const mailinfo & mi = state.maillog[i];
        !           686:     if (!mi.logged)
        !           687:       continue;
        !           688:     write_dev_state_line(f, "mail", i, "count", mi.logged);
        !           689:     write_dev_state_line(f, "mail", i, "first-sent-time", mi.firstsent);
        !           690:     write_dev_state_line(f, "mail", i, "last-sent-time", mi.lastsent);
        !           691:   }
        !           692: 
        !           693:   // ATA ONLY
        !           694:   write_dev_state_line(f, "ata-error-count", state.ataerrorcount);
        !           695: 
        !           696:   for (i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
        !           697:     const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
        !           698:     if (!pa.id)
        !           699:       continue;
        !           700:     write_dev_state_line(f, "ata-smart-attribute", i, "id", pa.id);
        !           701:     write_dev_state_line(f, "ata-smart-attribute", i, "val", pa.val);
        !           702:     write_dev_state_line(f, "ata-smart-attribute", i, "worst", pa.worst);
        !           703:     write_dev_state_line(f, "ata-smart-attribute", i, "raw", pa.raw);
        !           704:     write_dev_state_line(f, "ata-smart-attribute", i, "resvd", pa.resvd);
        !           705:   }
        !           706: 
        !           707:   return true;
        !           708: }
        !           709: 
        !           710: // Write to the attrlog file
        !           711: static bool write_dev_attrlog(const char * path, const persistent_dev_state & state)
        !           712: {
        !           713:   stdio_file f(path, "a");
        !           714:   if (!f) {
        !           715:     pout("Cannot create attribute log file \"%s\"\n", path);
        !           716:     return false;
        !           717:   }
        !           718: 
        !           719:   // ATA ONLY
        !           720:   time_t now = time(0);
        !           721:   struct tm * tms = gmtime(&now);
        !           722:   fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;",
        !           723:              1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday,
        !           724:              tms->tm_hour, tms->tm_min, tms->tm_sec);
        !           725:   for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
        !           726:     const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
        !           727:     if (!pa.id)
        !           728:       continue;
        !           729:     fprintf(f, "\t%d;%d;%"PRIu64";", pa.id, pa.val, pa.raw);
        !           730:   }
        !           731:   fprintf(f, "\n");
        !           732: 
        !           733:   return true;
        !           734: }
        !           735: 
        !           736: // Write all state files. If write_always is false, don't write
        !           737: // unless must_write is set.
        !           738: static void write_all_dev_states(const dev_config_vector & configs,
        !           739:                                  dev_state_vector & states,
        !           740:                                  bool write_always = true)
        !           741: {
        !           742:   for (unsigned i = 0; i < states.size(); i++) {
        !           743:     const dev_config & cfg = configs.at(i);
        !           744:     if (cfg.state_file.empty())
        !           745:       continue;
        !           746:     dev_state & state = states[i];
        !           747:     if (!write_always && !state.must_write)
        !           748:       continue;
        !           749:     if (!write_dev_state(cfg.state_file.c_str(), state))
        !           750:       continue;
        !           751:     state.must_write = false;
        !           752:     if (write_always || debugmode)
        !           753:       PrintOut(LOG_INFO, "Device: %s, state written to %s\n",
        !           754:                cfg.name.c_str(), cfg.state_file.c_str());
        !           755:   }
        !           756: }
        !           757: 
        !           758: // Write to all attrlog files
        !           759: static void write_all_dev_attrlogs(const dev_config_vector & configs,
        !           760:                                    dev_state_vector & states)
        !           761: {
        !           762:   for (unsigned i = 0; i < states.size(); i++) {
        !           763:     const dev_config & cfg = configs.at(i);
        !           764:     if (cfg.attrlog_file.empty())
        !           765:       continue;
        !           766:     dev_state & state = states[i];
        !           767:     write_dev_attrlog(cfg.attrlog_file.c_str(), state);
        !           768:   }
        !           769: }
        !           770: 
        !           771: // remove the PID file
        !           772: static void RemovePidFile()
        !           773: {
        !           774:   if (!pid_file.empty()) {
        !           775:     if (unlink(pid_file.c_str()))
        !           776:       PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", 
        !           777:                pid_file.c_str(), strerror(errno));
        !           778:     pid_file.clear();
        !           779:   }
        !           780:   return;
        !           781: }
        !           782: 
        !           783: extern "C" { // signal handlers require C-linkage
        !           784: 
        !           785: //  Note if we catch a SIGUSR1
        !           786: static void USR1handler(int sig)
        !           787: {
        !           788:   if (SIGUSR1==sig)
        !           789:     caughtsigUSR1=1;
        !           790:   return;
        !           791: }
        !           792: 
        !           793: #ifdef _WIN32
        !           794: //  Note if we catch a SIGUSR2
        !           795: static void USR2handler(int sig)
        !           796: {
        !           797:   if (SIGUSR2==sig)
        !           798:     caughtsigUSR2=1;
        !           799:   return;
        !           800: }
        !           801: #endif
        !           802: 
        !           803: // Note if we catch a HUP (or INT in debug mode)
        !           804: static void HUPhandler(int sig)
        !           805: {
        !           806:   if (sig==SIGHUP)
        !           807:     caughtsigHUP=1;
        !           808:   else
        !           809:     caughtsigHUP=2;
        !           810:   return;
        !           811: }
        !           812: 
        !           813: // signal handler for TERM, QUIT, and INT (if not in debug mode)
        !           814: static void sighandler(int sig)
        !           815: {
        !           816:   if (!caughtsigEXIT)
        !           817:     caughtsigEXIT=sig;
        !           818:   return;
        !           819: }
        !           820: 
        !           821: } // extern "C"
        !           822: 
        !           823: // Cleanup, print Goodbye message and remove pidfile
        !           824: static int Goodbye(int status)
        !           825: {
        !           826:   // delete PID file, if one was created
        !           827:   RemovePidFile();
        !           828: 
        !           829:   // if we are exiting because of a code bug, tell user
        !           830:   if (status==EXIT_BADCODE)
        !           831:         PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
        !           832: 
        !           833:   // and this should be the final output from smartd before it exits
        !           834:   PrintOut(status?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", status);
        !           835: 
        !           836:   return status;
        !           837: }
        !           838: 
        !           839: #define ENVLENGTH 1024
        !           840: 
        !           841: // a replacement for setenv() which is not available on all platforms.
        !           842: // Note that the string passed to putenv must not be freed or made
        !           843: // invalid, since a pointer to it is kept by putenv(). This means that
        !           844: // it must either be a static buffer or allocated off the heap. The
        !           845: // string can be freed if the environment variable is redefined or
        !           846: // deleted via another call to putenv(). So we keep these on the stack
        !           847: // as long as the popen() call is underway.
        !           848: static int exportenv(char *stackspace, const char *name, const char *value)
        !           849: {
        !           850:   snprintf(stackspace,ENVLENGTH, "%s=%s", name, value);
        !           851:   return putenv(stackspace);
        !           852: }
        !           853: 
        !           854: static char *dnsdomain(const char *hostname)
        !           855: {
        !           856:   char *p = NULL;
        !           857: #ifdef HAVE_GETADDRINFO
        !           858:   static char canon_name[NI_MAXHOST];
        !           859:   struct addrinfo *info = NULL;
        !           860:   struct addrinfo hints;
        !           861:   int err;
        !           862: 
        !           863:   memset(&hints, 0, sizeof(hints));
        !           864:   hints.ai_flags = AI_CANONNAME;
        !           865:   if ((err = getaddrinfo(hostname, NULL, &hints, &info)) || (!info)) {
        !           866:     PrintOut(LOG_CRIT, "Error retrieving getaddrinfo(%s): %s\n", hostname, gai_strerror(err));
        !           867:     return NULL;
        !           868:   }
        !           869:   if (info->ai_canonname) {
        !           870:     strncpy(canon_name, info->ai_canonname, sizeof(canon_name));
        !           871:     canon_name[NI_MAXHOST - 1] = '\0';
        !           872:     p = canon_name;
        !           873:     if ((p = strchr(canon_name, '.')))
        !           874:       p++;
        !           875:   }
        !           876:   freeaddrinfo(info);
        !           877: #elif HAVE_GETHOSTBYNAME
        !           878:   struct hostent *hp;
        !           879:   if ((hp = gethostbyname(hostname))) {
        !           880:     // Does this work if gethostbyname() returns an IPv6 name in
        !           881:     // colon/dot notation?  [BA]
        !           882:     if ((p = strchr(hp->h_name, '.')))
        !           883:       p++; // skip "."
        !           884:   }
        !           885: #else
        !           886:   ARGUSED(hostname);
        !           887: #endif
        !           888:   return p;
        !           889: }
        !           890: 
        !           891: #define EBUFLEN 1024
        !           892: 
        !           893: static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
        !           894:                         __attribute__ ((format (printf, 4, 5)));
        !           895: 
        !           896: // If either address or executable path is non-null then send and log
        !           897: // a warning email, or execute executable
        !           898: static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...){
        !           899:   char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024];
        !           900:   char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN];
        !           901:   char environ_strings[11][ENVLENGTH];
        !           902:   time_t epoch;
        !           903:   va_list ap;
        !           904:   const int day=24*3600;
        !           905:   int days=0;
        !           906:   const char * const whichfail[]={
        !           907:     "EmailTest",                  // 0
        !           908:     "Health",                     // 1
        !           909:     "Usage",                      // 2
        !           910:     "SelfTest",                   // 3
        !           911:     "ErrorCount",                 // 4
        !           912:     "FailedHealthCheck",          // 5
        !           913:     "FailedReadSmartData",        // 6
        !           914:     "FailedReadSmartErrorLog",    // 7
        !           915:     "FailedReadSmartSelfTestLog", // 8
        !           916:     "FailedOpenDevice",           // 9
        !           917:     "CurrentPendingSector",       // 10
        !           918:     "OfflineUncorrectableSector", // 11
        !           919:     "Temperature"                 // 12
        !           920:   };
        !           921:   
        !           922:   const char *unknown="[Unknown]";
        !           923: 
        !           924:   // See if user wants us to send mail
        !           925:   if (cfg.emailaddress.empty() && cfg.emailcmdline.empty())
        !           926:     return;
        !           927: 
        !           928:   std::string address = cfg.emailaddress;
        !           929:   const char * executable = cfg.emailcmdline.c_str();
        !           930: 
        !           931:   // which type of mail are we sending?
        !           932:   mailinfo * mail=(state.maillog)+which;
        !           933: 
        !           934:   // checks for sanity
        !           935:   if (cfg.emailfreq<1 || cfg.emailfreq>3) {
        !           936:     PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg.mailwarn->emailfreq=%d\n",cfg.emailfreq);
        !           937:     return;
        !           938:   }
        !           939:   if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
        !           940:     PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n",
        !           941:              which, (int)sizeof(whichfail));
        !           942:     return;
        !           943:   }
        !           944: 
        !           945:   // Return if a single warning mail has been sent.
        !           946:   if ((cfg.emailfreq==1) && mail->logged)
        !           947:     return;
        !           948: 
        !           949:   // Return if this is an email test and one has already been sent.
        !           950:   if (which == 0 && mail->logged)
        !           951:     return;
        !           952:   
        !           953:   // To decide if to send mail, we need to know what time it is.
        !           954:   epoch=time(NULL);
        !           955: 
        !           956:   // Return if less than one day has gone by
        !           957:   if (cfg.emailfreq==2 && mail->logged && epoch<(mail->lastsent+day))
        !           958:     return;
        !           959: 
        !           960:   // Return if less than 2^(logged-1) days have gone by
        !           961:   if (cfg.emailfreq==3 && mail->logged) {
        !           962:     days=0x01<<(mail->logged-1);
        !           963:     days*=day;
        !           964:     if  (epoch<(mail->lastsent+days))
        !           965:       return;
        !           966:   }
        !           967: 
        !           968: #ifdef HAVE_LIBCAP_NG
        !           969:   if (enable_capabilities) {
        !           970:     PrintOut(LOG_ERR, "Sending a mail was supressed. "
        !           971:              "Mails can't be send when capabilites are enabled\n");
        !           972:     return;
        !           973:   }
        !           974: #endif
        !           975: 
        !           976:   // record the time of this mail message, and the first mail message
        !           977:   if (!mail->logged)
        !           978:     mail->firstsent=epoch;
        !           979:   mail->lastsent=epoch;
        !           980:   
        !           981:   // get system host & domain names (not null terminated if length=MAX) 
        !           982: #ifdef HAVE_GETHOSTNAME
        !           983:   if (gethostname(hostname, 256))
        !           984:     strcpy(hostname, unknown);
        !           985:   else {
        !           986:     char *p=NULL;
        !           987:     hostname[255]='\0';
        !           988:     p = dnsdomain(hostname);
        !           989:     if (p && *p) {
        !           990:       strncpy(domainname, p, 255);
        !           991:       domainname[255]='\0';
        !           992:     } else
        !           993:       strcpy(domainname, unknown);
        !           994:   }
        !           995: #else
        !           996:   strcpy(hostname, unknown);
        !           997:   strcpy(domainname, unknown);
        !           998: #endif
        !           999:   
        !          1000: #ifdef HAVE_GETDOMAINNAME
        !          1001:   if (getdomainname(nisdomain, 256))
        !          1002:     strcpy(nisdomain, unknown);
        !          1003:   else
        !          1004:     nisdomain[255]='\0';
        !          1005: #else
        !          1006:   strcpy(nisdomain, unknown);
        !          1007: #endif
        !          1008:   
        !          1009:   // print warning string into message
        !          1010:   va_start(ap, fmt);
        !          1011:   vsnprintf(message, 256, fmt, ap);
        !          1012:   va_end(ap);
        !          1013: 
        !          1014:   // appropriate message about further information
        !          1015:   additional[0]=original[0]=further[0]='\0';
        !          1016:   if (which) {
        !          1017:     sprintf(further,"You can also use the smartctl utility for further investigation.\n");
        !          1018: 
        !          1019:     switch (cfg.emailfreq) {
        !          1020:     case 1:
        !          1021:       sprintf(additional,"No additional email messages about this problem will be sent.\n");
        !          1022:       break;
        !          1023:     case 2:
        !          1024:       sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n");
        !          1025:       break;
        !          1026:     case 3:
        !          1027:       sprintf(additional,"Another email message will be sent in %d days if the problem persists\n",
        !          1028:               (0x01)<<mail->logged);
        !          1029:       break;
        !          1030:     }
        !          1031:     if (cfg.emailfreq>1 && mail->logged) {
        !          1032:       dateandtimezoneepoch(dates, mail->firstsent);
        !          1033:       sprintf(original,"The original email about this issue was sent at %s\n", dates);
        !          1034:     }
        !          1035:   }
        !          1036:   
        !          1037:   snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname);
        !          1038: 
        !          1039:   // If the user has set cfg.emailcmdline, use that as mailer, else "mail" or "mailx".
        !          1040:   if (!*executable)
        !          1041: #ifdef DEFAULT_MAILER
        !          1042:     executable = DEFAULT_MAILER ;
        !          1043: #else
        !          1044: #ifndef _WIN32
        !          1045:     executable = "mail";
        !          1046: #else
        !          1047:     executable = "blat"; // http://blat.sourceforge.net/
        !          1048: #endif
        !          1049: #endif
        !          1050: 
        !          1051: #ifndef _WIN32 // blat mailer needs comma
        !          1052:   // replace commas by spaces to separate recipients
        !          1053:   std::replace(address.begin(), address.end(), ',', ' ');
        !          1054: #endif
        !          1055:   // Export information in environment variables that will be useful
        !          1056:   // for user scripts
        !          1057:   exportenv(environ_strings[0], "SMARTD_MAILER", executable);
        !          1058:   exportenv(environ_strings[1], "SMARTD_MESSAGE", message);
        !          1059:   exportenv(environ_strings[2], "SMARTD_SUBJECT", subject);
        !          1060:   dateandtimezoneepoch(dates, mail->firstsent);
        !          1061:   exportenv(environ_strings[3], "SMARTD_TFIRST", dates);
        !          1062:   snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent);
        !          1063:   exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates);
        !          1064:   exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]);
        !          1065:   if (!address.empty())
        !          1066:     exportenv(environ_strings[6], "SMARTD_ADDRESS", address.c_str());
        !          1067:   exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg.name.c_str());
        !          1068: 
        !          1069:   // Allow 'smartctl ... -d $SMARTD_DEVICETYPE $SMARTD_DEVICE'
        !          1070:   exportenv(environ_strings[8], "SMARTD_DEVICETYPE",
        !          1071:             (!cfg.dev_type.empty() ? cfg.dev_type.c_str() : "auto"));
        !          1072:   exportenv(environ_strings[9], "SMARTD_DEVICE", cfg.dev_name.c_str());
        !          1073: 
        !          1074:   snprintf(fullmessage, 1024,
        !          1075:              "This email was generated by the smartd daemon running on:\n\n"
        !          1076:              "   host name: %s\n"
        !          1077:              "  DNS domain: %s\n"
        !          1078:              "  NIS domain: %s\n\n"
        !          1079:              "The following warning/error was logged by the smartd daemon:\n\n"
        !          1080:              "%s\n\n"
        !          1081:              "For details see host's SYSLOG.\n\n"
        !          1082:              "%s%s%s",
        !          1083:             hostname, domainname, nisdomain, message, further, original, additional);
        !          1084:   exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage);
        !          1085: 
        !          1086:   // now construct a command to send this as EMAIL
        !          1087: #ifndef _WIN32
        !          1088:   if (!address.empty())
        !          1089:     snprintf(command, 2048, 
        !          1090:              "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n"
        !          1091:             "%sENDMAIL\n", subject, address.c_str(), fullmessage);
        !          1092:   else
        !          1093:     snprintf(command, 2048, "%s 2>&1", executable);
        !          1094:   
        !          1095:   // tell SYSLOG what we are about to do...
        !          1096:   const char * newadd = (!address.empty()? address.c_str() : "<nomailer>");
        !          1097:   const char * newwarn = (which? "Warning via" : "Test of");
        !          1098: 
        !          1099:   PrintOut(LOG_INFO,"%s %s to %s ...\n",
        !          1100:            which?"Sending warning via":"Executing test of", executable, newadd);
        !          1101:   
        !          1102:   // issue the command to send mail or to run the user's executable
        !          1103:   errno=0;
        !          1104:   FILE * pfp;
        !          1105:   if (!(pfp=popen(command, "r")))
        !          1106:     // failed to popen() mail process
        !          1107:     PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", 
        !          1108:             newwarn,  executable, newadd, errno?strerror(errno):"");
        !          1109:   else {
        !          1110:     // pipe suceeded!
        !          1111:     int len, status;
        !          1112:     char buffer[EBUFLEN];
        !          1113: 
        !          1114:     // if unexpected output on stdout/stderr, null terminate, print, and flush
        !          1115:     if ((len=fread(buffer, 1, EBUFLEN, pfp))) {
        !          1116:       int count=0;
        !          1117:       int newlen = len<EBUFLEN ? len : EBUFLEN-1;
        !          1118:       buffer[newlen]='\0';
        !          1119:       PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n", 
        !          1120:               newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer);
        !          1121:       
        !          1122:       // flush pipe if needed
        !          1123:       while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN)
        !          1124:        count++;
        !          1125: 
        !          1126:       // tell user that pipe was flushed, or that something is really wrong
        !          1127:       if (count && count<EBUFLEN)
        !          1128:        PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n", 
        !          1129:                 newwarn, executable, newadd);
        !          1130:       else if (count)
        !          1131:        PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n", 
        !          1132:                 newwarn, executable, newadd);
        !          1133:     }
        !          1134:     
        !          1135:     // if something went wrong with mail process, print warning
        !          1136:     errno=0;
        !          1137:     if (-1==(status=pclose(pfp)))
        !          1138:       PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd,
        !          1139:               errno?strerror(errno):"");
        !          1140:     else {
        !          1141:       // mail process apparently succeeded. Check and report exit status
        !          1142:       int status8;
        !          1143: 
        !          1144:       if (WIFEXITED(status)) {
        !          1145:        // exited 'normally' (but perhaps with nonzero status)
        !          1146:        status8=WEXITSTATUS(status);
        !          1147:        
        !          1148:        if (status8>128)  
        !          1149:          PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", 
        !          1150:                   newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128));
        !          1151:        else if (status8)  
        !          1152:          PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", 
        !          1153:                   newwarn, executable, newadd, status, status8);
        !          1154:        else
        !          1155:          PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
        !          1156:       }
        !          1157:       
        !          1158:       if (WIFSIGNALED(status))
        !          1159:        PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", 
        !          1160:                 newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status)));
        !          1161:       
        !          1162:       // this branch is probably not possible. If subprocess is
        !          1163:       // stopped then pclose() should not return.
        !          1164:       if (WIFSTOPPED(status)) 
        !          1165:        PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n",
        !          1166:                 newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status)));
        !          1167:       
        !          1168:     }
        !          1169:   }
        !          1170:   
        !          1171: #else // _WIN32
        !          1172: 
        !          1173:   // No "here-documents" on Windows, so must use separate commandline and stdin
        !          1174:   char stdinbuf[1024];
        !          1175:   command[0] = stdinbuf[0] = 0;
        !          1176:   int boxtype = -1, boxmsgoffs = 0;
        !          1177:   const char * newadd = "<nomailer>";
        !          1178:   if (!address.empty()) {
        !          1179:     // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox
        !          1180:     char addr1[9+1+13] = ""; int n1 = -1, n2 = -1;
        !          1181:     if (sscanf(address.c_str(), "%9[a-z]%n,%n", addr1, &n1, &n2) == 1 && (n1 == (int)address.size() || n2 > 0)) {
        !          1182:       if (!strcmp(addr1, "msgbox"))
        !          1183:         boxtype = 0;
        !          1184:       else if (!strcmp(addr1, "sysmsgbox"))
        !          1185:         boxtype = 1;
        !          1186:       if (boxtype >= 0)
        !          1187:         address.erase(0, (n2 > n1 ? n2 : n1));
        !          1188:     }
        !          1189: 
        !          1190:     if (!address.empty()) {
        !          1191:       // Use "blat" parameter syntax (TODO: configure via -M for other mailers)
        !          1192:       snprintf(command, sizeof(command),
        !          1193:                "%s - -q -subject \"%s\" -to \"%s\"",
        !          1194:                executable, subject, address.c_str());
        !          1195:       newadd = address.c_str();
        !          1196:     }
        !          1197: 
        !          1198:     // Message for mail [0...] and messagebox [boxmsgoffs...]
        !          1199:     snprintf(stdinbuf, sizeof(stdinbuf),
        !          1200:              "This email was generated by the smartd daemon running on:\n\n"
        !          1201:              "   host name: %s\n"
        !          1202:              "  DNS domain: %s\n"
        !          1203: //           "  NIS domain: %s\n"
        !          1204:              "\n",
        !          1205:              hostname, /*domainname, */ nisdomain);
        !          1206:     boxmsgoffs = strlen(stdinbuf);
        !          1207:     snprintf(stdinbuf+boxmsgoffs, sizeof(stdinbuf)-boxmsgoffs,
        !          1208:              "The following warning/error was logged by the smartd daemon:\n\n"
        !          1209:              "%s\n\n"
        !          1210:              "For details see the event log or log file of smartd.\n\n"
        !          1211:              "%s%s%s"
        !          1212:              "\n",
        !          1213:              message, further, original, additional);
        !          1214:   }
        !          1215:   else
        !          1216:     snprintf(command, sizeof(command), "%s", executable);
        !          1217: 
        !          1218:   const char * newwarn = (which ? "Warning via" : "Test of");
        !          1219:   if (boxtype >= 0) {
        !          1220:     // show message box
        !          1221:     daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs);
        !          1222:     PrintOut(LOG_INFO,"%s message box\n", newwarn);
        !          1223:   }
        !          1224:   if (command[0]) {
        !          1225:     char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog()
        !          1226:     int rc;
        !          1227:     // run command
        !          1228:     PrintOut(LOG_INFO,"%s %s to %s ...\n",
        !          1229:              (which?"Sending warning via":"Executing test of"), executable, newadd);
        !          1230:     rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf));
        !          1231:     if (rc >= 0 && stdoutbuf[0])
        !          1232:       PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n",
        !          1233:         newwarn, executable, newadd, (int)strlen(stdoutbuf), stdoutbuf);
        !          1234:     if (rc != 0)
        !          1235:       PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n",
        !          1236:         newwarn, executable, newadd, rc);
        !          1237:     else
        !          1238:       PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
        !          1239:   }
        !          1240: 
        !          1241: #endif // _WIN32
        !          1242: 
        !          1243:   // increment mail sent counter
        !          1244:   mail->logged++;
        !          1245: }
        !          1246: 
        !          1247: static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
        !          1248:                                __attribute__ ((format (printf, 4, 5)));
        !          1249: 
        !          1250: static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
        !          1251: {
        !          1252:   if (!(0 <= which && which < SMARTD_NMAIL))
        !          1253:     return;
        !          1254: 
        !          1255:   // Return if no mail sent yet
        !          1256:   mailinfo & mi = state.maillog[which];
        !          1257:   if (!mi.logged)
        !          1258:     return;
        !          1259: 
        !          1260:   // Format & print message
        !          1261:   char msg[256];
        !          1262:   va_list ap;
        !          1263:   va_start(ap, fmt);
        !          1264:   vsnprintf(msg, sizeof(msg), fmt, ap);
        !          1265:   va_end(ap);
        !          1266: 
        !          1267:   PrintOut(LOG_INFO, "Device: %s, %s, warning condition reset after %d email%s\n", cfg.name.c_str(),
        !          1268:            msg, mi.logged, (mi.logged==1 ? "" : "s"));
        !          1269: 
        !          1270:   // Clear mail counter and timestamps
        !          1271:   mi = mailinfo();
        !          1272:   state.must_write = true;
        !          1273: }
        !          1274: 
        !          1275: #ifndef _WIN32
        !          1276: 
        !          1277: // Output multiple lines via separate syslog(3) calls.
        !          1278: static void vsyslog_lines(int priority, const char * fmt, va_list ap)
        !          1279: {
        !          1280:   char buf[512+EBUFLEN]; // enough space for exec cmd output in MailWarning()
        !          1281:   vsnprintf(buf, sizeof(buf), fmt, ap);
        !          1282: 
        !          1283:   for (char * p = buf, * q; p && *p; p = q) {
        !          1284:     if ((q = strchr(p, '\n')))
        !          1285:       *q++ = 0;
        !          1286:     if (*p)
        !          1287:       syslog(priority, "%s\n", p);
        !          1288:   }
        !          1289: }
        !          1290: 
        !          1291: #else  // _WIN32
        !          1292: // os_win32/syslog_win32.cpp supports multiple lines.
        !          1293: #define vsyslog_lines vsyslog
        !          1294: #endif // _WIN32
        !          1295: 
        !          1296: // Printing function for watching ataprint commands, or losing them
        !          1297: // [From GLIBC Manual: Since the prototype doesn't specify types for
        !          1298: // optional arguments, in a call to a variadic function the default
        !          1299: // argument promotions are performed on the optional argument
        !          1300: // values. This means the objects of type char or short int (whether
        !          1301: // signed or not) are promoted to either int or unsigned int, as
        !          1302: // appropriate.]
        !          1303: void pout(const char *fmt, ...){
        !          1304:   va_list ap;
        !          1305: 
        !          1306:   // get the correct time in syslog()
        !          1307:   FixGlibcTimeZoneBug();
        !          1308:   // initialize variable argument list 
        !          1309:   va_start(ap,fmt);
        !          1310:   // in debugmode==1 mode we will print the output from the ataprint.o functions!
        !          1311:   if (debugmode && debugmode!=2)
        !          1312: #ifdef _WIN32
        !          1313:    if (facility == LOG_LOCAL1) // logging to stdout
        !          1314:     vfprintf(stderr,fmt,ap);
        !          1315:    else   
        !          1316: #endif
        !          1317:     vprintf(fmt,ap);
        !          1318:   // in debugmode==2 mode we print output from knowndrives.o functions
        !          1319:   else if (debugmode==2 || ata_debugmode || scsi_debugmode) {
        !          1320:     openlog("smartd", LOG_PID, facility);
        !          1321:     vsyslog_lines(LOG_INFO, fmt, ap);
        !          1322:     closelog();
        !          1323:   }
        !          1324:   va_end(ap);
        !          1325:   fflush(NULL);
        !          1326:   return;
        !          1327: }
        !          1328: 
        !          1329: // This function prints either to stdout or to the syslog as needed.
        !          1330: static void PrintOut(int priority, const char *fmt, ...){
        !          1331:   va_list ap;
        !          1332:   
        !          1333:   // get the correct time in syslog()
        !          1334:   FixGlibcTimeZoneBug();
        !          1335:   // initialize variable argument list 
        !          1336:   va_start(ap,fmt);
        !          1337:   if (debugmode) 
        !          1338: #ifdef _WIN32
        !          1339:    if (facility == LOG_LOCAL1) // logging to stdout
        !          1340:     vfprintf(stderr,fmt,ap);
        !          1341:    else   
        !          1342: #endif
        !          1343:     vprintf(fmt,ap);
        !          1344:   else {
        !          1345:     openlog("smartd", LOG_PID, facility);
        !          1346:     vsyslog_lines(priority, fmt, ap);
        !          1347:     closelog();
        !          1348:   }
        !          1349:   va_end(ap);
        !          1350:   return;
        !          1351: }
        !          1352: 
        !          1353: // Used to warn users about invalid checksums. Called from atacmds.cpp.
        !          1354: void checksumwarning(const char * string)
        !          1355: {
        !          1356:   pout("Warning! %s error: invalid SMART checksum.\n", string);
        !          1357: }
        !          1358: 
        !          1359: #ifndef _WIN32
        !          1360: 
        !          1361: // Wait for the pid file to show up, this makes sure a calling program knows
        !          1362: // that the daemon is really up and running and has a pid to kill it
        !          1363: static bool WaitForPidFile()
        !          1364: {
        !          1365:     int waited, max_wait = 10;
        !          1366:     struct stat stat_buf;
        !          1367: 
        !          1368:     if (pid_file.empty() || debugmode)
        !          1369:        return true;
        !          1370: 
        !          1371:     for(waited = 0; waited < max_wait; ++waited) {
        !          1372:        if (!stat(pid_file.c_str(), &stat_buf)) {
        !          1373:                return true;
        !          1374:        } else
        !          1375:                sleep(1);
        !          1376:     }
        !          1377:     return false;
        !          1378: }
        !          1379: 
        !          1380: #endif // _WIN32
        !          1381: 
        !          1382: // Forks new process, closes ALL file descriptors, redirects stdin,
        !          1383: // stdout, and stderr.  Not quite daemon().  See
        !          1384: // http://www.linuxjournal.com/article/2335
        !          1385: // for a good description of why we do things this way.
        !          1386: static void DaemonInit()
        !          1387: {
        !          1388: #ifndef _WIN32
        !          1389:   pid_t pid;
        !          1390:   int i;  
        !          1391: 
        !          1392:   // flush all buffered streams.  Else we might get two copies of open
        !          1393:   // streams since both parent and child get copies of the buffers.
        !          1394:   fflush(NULL);
        !          1395: 
        !          1396:   if (do_fork) {
        !          1397:     if ((pid=fork()) < 0) {
        !          1398:       // unable to fork!
        !          1399:       PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
        !          1400:       EXIT(EXIT_STARTUP);
        !          1401:     }
        !          1402:     else if (pid) {
        !          1403:       // we are the parent process, wait for pid file, then exit cleanly
        !          1404:       if(!WaitForPidFile()) {
        !          1405:         PrintOut(LOG_CRIT,"PID file %s didn't show up!\n", pid_file.c_str());
        !          1406:        EXIT(EXIT_STARTUP);
        !          1407:       } else
        !          1408:         EXIT(0);
        !          1409:     }
        !          1410:   
        !          1411:     // from here on, we are the child process.
        !          1412:     setsid();
        !          1413: 
        !          1414:     // Fork one more time to avoid any possibility of having terminals
        !          1415:     if ((pid=fork()) < 0) {
        !          1416:       // unable to fork!
        !          1417:       PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
        !          1418:       EXIT(EXIT_STARTUP);
        !          1419:     }
        !          1420:     else if (pid)
        !          1421:       // we are the parent process -- exit cleanly
        !          1422:       EXIT(0);
        !          1423: 
        !          1424:     // Now we are the child's child...
        !          1425:   }
        !          1426: 
        !          1427:   // close any open file descriptors
        !          1428:   for (i=getdtablesize();i>=0;--i)
        !          1429:     close(i);
        !          1430:   
        !          1431: #define NO_warn_unused_result(cmd) { if (cmd) {} ; }
        !          1432: 
        !          1433:   // redirect any IO attempts to /dev/null for stdin
        !          1434:   i=open("/dev/null",O_RDWR);
        !          1435:   if (i>=0) {
        !          1436:     // stdout
        !          1437:     NO_warn_unused_result(dup(i));
        !          1438:     // stderr
        !          1439:     NO_warn_unused_result(dup(i));
        !          1440:   };
        !          1441:   umask(0022);
        !          1442:   NO_warn_unused_result(chdir("/"));
        !          1443: 
        !          1444:   if (do_fork)
        !          1445:     PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());
        !          1446: 
        !          1447: #else // _WIN32
        !          1448: 
        !          1449:   // No fork() on native Win32
        !          1450:   // Detach this process from console
        !          1451:   fflush(NULL);
        !          1452:   if (daemon_detach("smartd")) {
        !          1453:     PrintOut(LOG_CRIT,"smartd unable to detach from console!\n");
        !          1454:     EXIT(EXIT_STARTUP);
        !          1455:   }
        !          1456:   // stdin/out/err now closed if not redirected
        !          1457: 
        !          1458: #endif // _WIN32
        !          1459:   return;
        !          1460: }
        !          1461: 
        !          1462: // create a PID file containing the current process id
        !          1463: static void WritePidFile()
        !          1464: {
        !          1465:   if (!pid_file.empty()) {
        !          1466:     pid_t pid = getpid();
        !          1467:     mode_t old_umask;
        !          1468: #ifndef __CYGWIN__
        !          1469:     old_umask = umask(0077); // rwx------
        !          1470: #else
        !          1471:     // Cygwin: smartd service runs on system account, ensure PID file can be read by admins
        !          1472:     old_umask = umask(0033); // rwxr--r--
        !          1473: #endif
        !          1474: 
        !          1475:     stdio_file f(pid_file.c_str(), "w");
        !          1476:     umask(old_umask);
        !          1477:     if (!(f && fprintf(f, "%d\n", (int)pid) > 0 && f.close())) {
        !          1478:       PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file.c_str());
        !          1479:       EXIT(EXIT_PID);
        !          1480:     }
        !          1481:     PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file.c_str(), (int)pid);
        !          1482:   }
        !          1483: }
        !          1484: 
        !          1485: // Prints header identifying version of code and home
        !          1486: static void PrintHead()
        !          1487: {
        !          1488:   PrintOut(LOG_INFO, "%s\n", format_version_info("smartd").c_str());
        !          1489: }
        !          1490: 
        !          1491: // prints help info for configuration file Directives
        !          1492: static void Directives()
        !          1493: {
        !          1494:   PrintOut(LOG_INFO,
        !          1495:            "Configuration file (%s) Directives (after device name):\n"
        !          1496:            "  -d TYPE Set the device type: %s, auto, removable\n"
        !          1497:            "  -T TYPE Set the tolerance to one of: normal, permissive\n"
        !          1498:            "  -o VAL  Enable/disable automatic offline tests (on/off)\n"
        !          1499:            "  -S VAL  Enable/disable attribute autosave (on/off)\n"
        !          1500:            "  -n MODE No check if: never, sleep[,N][,q], standby[,N][,q], idle[,N][,q]\n"
        !          1501:            "  -H      Monitor SMART Health Status, report if failed\n"
        !          1502:            "  -s REG  Do Self-Test at time(s) given by regular expression REG\n"
        !          1503:            "  -l TYPE Monitor SMART log or self-test status\n"
        !          1504:            "          Type is one of: error, selftest, xerror, offlinests, selfteststs\n"
        !          1505:            "  -l scterc,R,W  Set SCT Error Recovery Control\n"
        !          1506:            "  -f      Monitor 'Usage' Attributes, report failures\n"
        !          1507:            "  -m ADD  Send email warning to address ADD\n"
        !          1508:            "  -M TYPE Modify email warning behavior (see man page)\n"
        !          1509:            "  -p      Report changes in 'Prefailure' Attributes\n"
        !          1510:            "  -u      Report changes in 'Usage' Attributes\n"
        !          1511:            "  -t      Equivalent to -p and -u Directives\n"
        !          1512:            "  -r ID   Also report Raw values of Attribute ID with -p, -u or -t\n"
        !          1513:            "  -R ID   Track changes in Attribute ID Raw value with -p, -u or -t\n"
        !          1514:            "  -i ID   Ignore Attribute ID for -f Directive\n"
        !          1515:            "  -I ID   Ignore Attribute ID for -p, -u or -t Directive\n"
        !          1516:            "  -C ID[+] Monitor [increases of] Current Pending Sectors in Attribute ID\n"
        !          1517:            "  -U ID[+] Monitor [increases of] Offline Uncorrectable Sectors in Attribute ID\n"
        !          1518:            "  -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit\n"
        !          1519:            "  -v N,ST Modifies labeling of Attribute N (see man page)  \n"
        !          1520:            "  -P TYPE Drive-specific presets: use, ignore, show, showall\n"
        !          1521:            "  -a      Default: -H -f -t -l error -l selftest -l selfteststs -C 197 -U 198\n"
        !          1522:            "  -F TYPE Firmware bug workaround: none, samsung, samsung2, samsung3\n"
        !          1523:            "   #      Comment: text after a hash sign is ignored\n"
        !          1524:            "   \\      Line continuation character\n"
        !          1525:            "Attribute ID is a decimal integer 1 <= ID <= 255\n"
        !          1526:           "Use ID = 0 to turn off -C and/or -U Directives\n"
        !          1527:            "Example: /dev/hda -a\n", 
        !          1528:            configfile, smi()->get_valid_dev_types_str().c_str());
        !          1529:   return;
        !          1530: }
        !          1531: 
        !          1532: /* Returns a pointer to a static string containing a formatted list of the valid
        !          1533:    arguments to the option opt or NULL on failure. */
        !          1534: static const char *GetValidArgList(char opt)
        !          1535: {
        !          1536:   switch (opt) {
        !          1537:   case 'A':
        !          1538:   case 's':
        !          1539:     return "<PATH_PREFIX>";
        !          1540:   case 'c':
        !          1541:     return "<FILE_NAME>, -";
        !          1542:   case 'l':
        !          1543:     return "daemon, local0, local1, local2, local3, local4, local5, local6, local7";
        !          1544:   case 'q':
        !          1545:     return "nodev, errors, nodevstartup, never, onecheck, showtests";
        !          1546:   case 'r':
        !          1547:     return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
        !          1548:   case 'B':
        !          1549:   case 'p':
        !          1550:     return "<FILE_NAME>";
        !          1551:   case 'i':
        !          1552:     return "<INTEGER_SECONDS>";
        !          1553:   default:
        !          1554:     return NULL;
        !          1555:   }
        !          1556: }
        !          1557: 
        !          1558: /* prints help information for command syntax */
        !          1559: static void Usage()
        !          1560: {
        !          1561:   PrintOut(LOG_INFO,"Usage: smartd [options]\n\n");
        !          1562:   PrintOut(LOG_INFO,"  -A PREFIX, --attributelog=PREFIX\n");
        !          1563:   PrintOut(LOG_INFO,"        Log ATA attribute information to {PREFIX}MODEL-SERIAL.ata.csv\n");
        !          1564: #ifdef SMARTMONTOOLS_ATTRIBUTELOG
        !          1565:   PrintOut(LOG_INFO,"        [default is "SMARTMONTOOLS_ATTRIBUTELOG"MODEL-SERIAL.ata.csv]\n");
        !          1566: #endif
        !          1567:   PrintOut(LOG_INFO,"\n");
        !          1568:   PrintOut(LOG_INFO,"  -B [+]FILE, --drivedb=[+]FILE\n");
        !          1569:   PrintOut(LOG_INFO,"        Read and replace [add] drive database from FILE\n");
        !          1570:   PrintOut(LOG_INFO,"        [default is +%s", get_drivedb_path_add());
        !          1571: #ifdef SMARTMONTOOLS_DRIVEDBDIR
        !          1572:   PrintOut(LOG_INFO,"\n");
        !          1573:   PrintOut(LOG_INFO,"         and then    %s", get_drivedb_path_default());
        !          1574: #endif
        !          1575:   PrintOut(LOG_INFO,"]\n\n");
        !          1576:   PrintOut(LOG_INFO,"  -c NAME|-, --configfile=NAME|-\n");
        !          1577:   PrintOut(LOG_INFO,"        Read configuration file NAME or stdin\n");
        !          1578:   PrintOut(LOG_INFO,"        [default is %s]\n\n", configfile);
        !          1579: #ifdef HAVE_LIBCAP_NG
        !          1580:   PrintOut(LOG_INFO,"  -C, --capabilities\n");
        !          1581:   PrintOut(LOG_INFO,"        Use capabilities.\n"
        !          1582:                     "        Warning: Mail notification does not work when used.\n\n");
        !          1583: #endif
        !          1584:   PrintOut(LOG_INFO,"  -d, --debug\n");
        !          1585:   PrintOut(LOG_INFO,"        Start smartd in debug mode\n\n");
        !          1586:   PrintOut(LOG_INFO,"  -D, --showdirectives\n");
        !          1587:   PrintOut(LOG_INFO,"        Print the configuration file Directives and exit\n\n");
        !          1588:   PrintOut(LOG_INFO,"  -h, --help, --usage\n");
        !          1589:   PrintOut(LOG_INFO,"        Display this help and exit\n\n");
        !          1590:   PrintOut(LOG_INFO,"  -i N, --interval=N\n");
        !          1591:   PrintOut(LOG_INFO,"        Set interval between disk checks to N seconds, where N >= 10\n\n");
        !          1592:   PrintOut(LOG_INFO,"  -l local[0-7], --logfacility=local[0-7]\n");
        !          1593: #ifndef _WIN32
        !          1594:   PrintOut(LOG_INFO,"        Use syslog facility local0 - local7 or daemon [default]\n\n");
        !          1595: #else
        !          1596:   PrintOut(LOG_INFO,"        Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n");
        !          1597: #endif
        !          1598: #ifndef _WIN32
        !          1599:   PrintOut(LOG_INFO,"  -n, --no-fork\n");
        !          1600:   PrintOut(LOG_INFO,"        Do not fork into background\n\n");
        !          1601: #endif  // _WIN32
        !          1602:   PrintOut(LOG_INFO,"  -p NAME, --pidfile=NAME\n");
        !          1603:   PrintOut(LOG_INFO,"        Write PID file NAME\n\n");
        !          1604:   PrintOut(LOG_INFO,"  -q WHEN, --quit=WHEN\n");
        !          1605:   PrintOut(LOG_INFO,"        Quit on one of: %s\n\n", GetValidArgList('q'));
        !          1606:   PrintOut(LOG_INFO,"  -r, --report=TYPE\n");
        !          1607:   PrintOut(LOG_INFO,"        Report transactions for one of: %s\n\n", GetValidArgList('r'));
        !          1608:   PrintOut(LOG_INFO,"  -s PREFIX, --savestates=PREFIX\n");
        !          1609:   PrintOut(LOG_INFO,"        Save disk states to {PREFIX}MODEL-SERIAL.TYPE.state\n");
        !          1610: #ifdef SMARTMONTOOLS_SAVESTATES
        !          1611:   PrintOut(LOG_INFO,"        [default is "SMARTMONTOOLS_SAVESTATES"MODEL-SERIAL.TYPE.state]\n");
        !          1612: #endif
        !          1613:   PrintOut(LOG_INFO,"\n");
        !          1614: #ifdef _WIN32
        !          1615:   PrintOut(LOG_INFO,"  --service\n");
        !          1616:   PrintOut(LOG_INFO,"        Running as windows service (see man page), install with:\n");
        !          1617:   PrintOut(LOG_INFO,"          smartd install [options]\n");
        !          1618:   PrintOut(LOG_INFO,"        Remove service with:\n");
        !          1619:   PrintOut(LOG_INFO,"          smartd remove\n\n");
        !          1620: #endif // _WIN32
        !          1621:   PrintOut(LOG_INFO,"  -V, --version, --license, --copyright\n");
        !          1622:   PrintOut(LOG_INFO,"        Print License, Copyright, and version information\n");
        !          1623: }
        !          1624: 
        !          1625: static int CloseDevice(smart_device * device, const char * name)
        !          1626: {
        !          1627:   if (!device->close()){
        !          1628:     PrintOut(LOG_INFO,"Device: %s, %s, close() failed\n", name, device->get_errmsg());
        !          1629:     return 1;
        !          1630:   }
        !          1631:   // device sucessfully closed
        !          1632:   return 0;
        !          1633: }
        !          1634: 
        !          1635: // return true if a char is not allowed in a state file name
        !          1636: static bool not_allowed_in_filename(char c)
        !          1637: {
        !          1638:   return !(   ('0' <= c && c <= '9')
        !          1639:            || ('A' <= c && c <= 'Z')
        !          1640:            || ('a' <= c && c <= 'z'));
        !          1641: }
        !          1642: 
        !          1643: // Read error count from Summary or Extended Comprehensive SMART error log
        !          1644: // Return -1 on error
        !          1645: static int read_ata_error_count(ata_device * device, const char * name,
        !          1646:                                 unsigned char fix_firmwarebug, bool extended)
        !          1647: {
        !          1648:   if (!extended) {
        !          1649:     ata_smart_errorlog log;
        !          1650:     if (ataReadErrorLog(device, &log, fix_firmwarebug)){
        !          1651:       PrintOut(LOG_INFO,"Device: %s, Read Summary SMART Error Log failed\n",name);
        !          1652:       return -1;
        !          1653:     }
        !          1654:     return (log.error_log_pointer ? log.ata_error_count : 0);
        !          1655:   }
        !          1656:   else {
        !          1657:     ata_smart_exterrlog logx;
        !          1658:     if (!ataReadExtErrorLog(device, &logx, 1 /*first sector only*/)) {
        !          1659:       PrintOut(LOG_INFO,"Device: %s, Read Extended Comprehensive SMART Error Log failed\n",name);
        !          1660:       return -1;
        !          1661:     }
        !          1662:     // Some disks use the reserved byte as index, see ataprint.cpp.
        !          1663:     return (logx.error_log_index || logx.reserved1 ? logx.device_error_count : 0);
        !          1664:   }
        !          1665: }
        !          1666: 
        !          1667: // returns <0 if problem.  Otherwise, bottom 8 bits are the self test
        !          1668: // error count, and top bits are the power-on hours of the last error.
        !          1669: static int SelfTestErrorCount(ata_device * device, const char * name,
        !          1670:                               unsigned char fix_firmwarebug)
        !          1671: {
        !          1672:   struct ata_smart_selftestlog log;
        !          1673: 
        !          1674:   if (ataReadSelfTestLog(device, &log, fix_firmwarebug)){
        !          1675:     PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
        !          1676:     return -1;
        !          1677:   }
        !          1678:   
        !          1679:   // return current number of self-test errors
        !          1680:   return ataPrintSmartSelfTestlog(&log, false, fix_firmwarebug);
        !          1681: }
        !          1682: 
        !          1683: #define SELFTEST_ERRORCOUNT(x) (x & 0xff)
        !          1684: #define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff)
        !          1685: 
        !          1686: // Log offline data collection status
        !          1687: static void log_offline_data_coll_status(const char * name, unsigned char status)
        !          1688: {
        !          1689:   const char * msg;
        !          1690:   switch (status & 0x7f) {
        !          1691:     case 0x00: msg = "was never started"; break;
        !          1692:     case 0x02: msg = "was completed without error"; break;
        !          1693:     case 0x03: msg = "is in progress"; break;
        !          1694:     case 0x04: msg = "was suspended by an interrupting command from host"; break;
        !          1695:     case 0x05: msg = "was aborted by an interrupting command from host"; break;
        !          1696:     case 0x06: msg = "was aborted by the device with a fatal error"; break;
        !          1697:     default:   msg = 0;
        !          1698:   }
        !          1699: 
        !          1700:   if (msg)
        !          1701:     PrintOut(((status & 0x7f) == 0x06 ? LOG_CRIT : LOG_INFO),
        !          1702:              "Device: %s, offline data collection %s%s\n", name, msg,
        !          1703:              ((status & 0x80) ? " (auto:on)" : ""));
        !          1704:   else
        !          1705:     PrintOut(LOG_INFO, "Device: %s, unknown offline data collection status 0x%02x\n",
        !          1706:              name, status);
        !          1707: }
        !          1708: 
        !          1709: // Log self-test execution status
        !          1710: static void log_self_test_exec_status(const char * name, unsigned char status)
        !          1711: {
        !          1712:   const char * msg;
        !          1713:   switch (status >> 4) {
        !          1714:     case 0x0: msg = "completed without error"; break;
        !          1715:     case 0x1: msg = "was aborted by the host"; break;
        !          1716:     case 0x2: msg = "was interrupted by the host with a reset"; break;
        !          1717:     case 0x3: msg = "could not complete due to a fatal or unknown error"; break;
        !          1718:     case 0x4: msg = "completed with error (unknown test element)"; break;
        !          1719:     case 0x5: msg = "completed with error (electrical test element)"; break;
        !          1720:     case 0x6: msg = "completed with error (servo/seek test element)"; break;
        !          1721:     case 0x7: msg = "completed with error (read test element)"; break;
        !          1722:     case 0x8: msg = "completed with error (handling damage?)"; break;
        !          1723:     default:  msg = 0;
        !          1724:   }
        !          1725: 
        !          1726:   if (msg)
        !          1727:     PrintOut(((status >> 4) >= 0x4 ? LOG_CRIT : LOG_INFO),
        !          1728:              "Device: %s, previous self-test %s\n", name, msg);
        !          1729:   else if ((status >> 4) == 0xf)
        !          1730:     PrintOut(LOG_INFO, "Device: %s, self-test in progress, %u0%% remaining\n",
        !          1731:              name, status & 0x0f);
        !          1732:   else
        !          1733:     PrintOut(LOG_INFO, "Device: %s, unknown self-test status 0x%02x\n",
        !          1734:              name, status);
        !          1735: }
        !          1736: 
        !          1737: // Check pending sector count id (-C, -U directives).
        !          1738: static bool check_pending_id(const dev_config & cfg, const dev_state & state,
        !          1739:                              unsigned char id, const char * msg)
        !          1740: {
        !          1741:   // Check attribute index
        !          1742:   int i = ata_find_attr_index(id, state.smartval);
        !          1743:   if (i < 0) {
        !          1744:     PrintOut(LOG_INFO, "Device: %s, can't monitor %s count - no Attribute %d\n",
        !          1745:              cfg.name.c_str(), msg, id);
        !          1746:     return false;
        !          1747:   }
        !          1748: 
        !          1749:   // Check value
        !          1750:   uint64_t rawval = ata_get_attr_raw_value(state.smartval.vendor_attributes[i],
        !          1751:     cfg.attribute_defs);
        !          1752:   if (rawval >= (state.num_sectors ? state.num_sectors : 0xffffffffULL)) {
        !          1753:     PrintOut(LOG_INFO, "Device: %s, ignoring %s count - bogus Attribute %d value %"PRIu64" (0x%"PRIx64")\n",
        !          1754:              cfg.name.c_str(), msg, id, rawval, rawval);
        !          1755:     return false;
        !          1756:   }
        !          1757: 
        !          1758:   return true;
        !          1759: }
        !          1760: 
        !          1761: // Called by ATA/SCSIDeviceScan() after successful device check
        !          1762: static void finish_device_scan(dev_config & cfg, dev_state & state)
        !          1763: {
        !          1764:   // Set cfg.emailfreq if user hasn't set it
        !          1765:   if ((!cfg.emailaddress.empty() || !cfg.emailcmdline.empty()) && !cfg.emailfreq) {
        !          1766:     // Avoid that emails are suppressed forever due to state persistence
        !          1767:     if (cfg.state_file.empty())
        !          1768:       cfg.emailfreq = 1; // '-M once'
        !          1769:     else
        !          1770:       cfg.emailfreq = 2; // '-M daily'
        !          1771:   }
        !          1772: 
        !          1773:   // Start self-test regex check now if time was not read from state file
        !          1774:   if (!cfg.test_regex.empty() && !state.scheduled_test_next_check)
        !          1775:     state.scheduled_test_next_check = time(0);
        !          1776: }
        !          1777: 
        !          1778: 
        !          1779: // TODO: Add '-F swapid' directive
        !          1780: const bool fix_swapped_id = false;
        !          1781: 
        !          1782: // scan to see what ata devices there are, and if they support SMART
        !          1783: static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atadev)
        !          1784: {
        !          1785:   int supported=0;
        !          1786:   struct ata_identify_device drive;
        !          1787:   const char *name = cfg.name.c_str();
        !          1788:   int retid;
        !          1789: 
        !          1790:   // Device must be open
        !          1791: 
        !          1792:   // Get drive identity structure
        !          1793:   if ((retid = ata_read_identity(atadev, &drive, fix_swapped_id))) {
        !          1794:     if (retid<0)
        !          1795:       // Unable to read Identity structure
        !          1796:       PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name);
        !          1797:     else
        !          1798:       PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n",
        !          1799:                name, packetdevicetype(retid-1));
        !          1800:     CloseDevice(atadev, name);
        !          1801:     return 2; 
        !          1802:   }
        !          1803: 
        !          1804:   // Log drive identity and size
        !          1805:   char model[40+1], serial[20+1], firmware[8+1];
        !          1806:   ata_format_id_string(model, drive.model, sizeof(model)-1);
        !          1807:   ata_format_id_string(serial, drive.serial_no, sizeof(serial)-1);
        !          1808:   ata_format_id_string(firmware, drive.fw_rev, sizeof(firmware)-1);
        !          1809: 
        !          1810:   ata_size_info sizes;
        !          1811:   ata_get_size_info(&drive, sizes);
        !          1812:   state.num_sectors = sizes.sectors;
        !          1813: 
        !          1814:   char wwn[30]; wwn[0] = 0;
        !          1815:   unsigned oui = 0; uint64_t unique_id = 0;
        !          1816:   int naa = ata_get_wwn(&drive, oui, unique_id);
        !          1817:   if (naa >= 0)
        !          1818:     snprintf(wwn, sizeof(wwn), "WWN:%x-%06x-%09"PRIx64", ", naa, oui, unique_id);
        !          1819: 
        !          1820:   char cap[32];
        !          1821:   PrintOut(LOG_INFO, "Device: %s, %s, S/N:%s, %sFW:%s, %s\n", name,
        !          1822:            model, serial, wwn, firmware,
        !          1823:            format_capacity(cap, sizeof(cap), sizes.capacity, "."));
        !          1824: 
        !          1825:   // Show if device in database, and use preset vendor attribute
        !          1826:   // options unless user has requested otherwise.
        !          1827:   if (cfg.ignorepresets)
        !          1828:     PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name);
        !          1829:   else {
        !          1830:     // Apply vendor specific presets, print warning if present
        !          1831:     const drive_settings * dbentry = lookup_drive_apply_presets(
        !          1832:       &drive, cfg.attribute_defs, cfg.fix_firmwarebug);
        !          1833:     if (!dbentry)
        !          1834:       PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name);
        !          1835:     else {
        !          1836:       PrintOut(LOG_INFO, "Device: %s, found in smartd database%s%s\n",
        !          1837:         name, (*dbentry->modelfamily ? ": " : "."), (*dbentry->modelfamily ? dbentry->modelfamily : ""));
        !          1838:       if (*dbentry->warningmsg)
        !          1839:         PrintOut(LOG_CRIT, "Device: %s, WARNING: %s\n", name, dbentry->warningmsg);
        !          1840:     }
        !          1841:   }
        !          1842: 
        !          1843:   // Set default '-C 197[+]' if no '-C ID' is specified.
        !          1844:   if (!cfg.curr_pending_set)
        !          1845:     cfg.curr_pending_id = get_unc_attr_id(false, cfg.attribute_defs, cfg.curr_pending_incr);
        !          1846:   // Set default '-U 198[+]' if no '-U ID' is specified.
        !          1847:   if (!cfg.offl_pending_set)
        !          1848:     cfg.offl_pending_id = get_unc_attr_id(true, cfg.attribute_defs, cfg.offl_pending_incr);
        !          1849: 
        !          1850:   // If requested, show which presets would be used for this drive
        !          1851:   if (cfg.showpresets) {
        !          1852:     int savedebugmode=debugmode;
        !          1853:     PrintOut(LOG_INFO, "Device %s: presets are:\n", name);
        !          1854:     if (!debugmode)
        !          1855:       debugmode=2;
        !          1856:     show_presets(&drive);
        !          1857:     debugmode=savedebugmode;
        !          1858:   }
        !          1859: 
        !          1860:   // see if drive supports SMART
        !          1861:   supported=ataSmartSupport(&drive);
        !          1862:   if (supported!=1) {
        !          1863:     if (supported==0)
        !          1864:       // drive does NOT support SMART
        !          1865:       PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name);
        !          1866:     else
        !          1867:       // can't tell if drive supports SMART
        !          1868:       PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name);
        !          1869:   
        !          1870:     // should we proceed anyway?
        !          1871:     if (cfg.permissive) {
        !          1872:       PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name);
        !          1873:     }
        !          1874:     else {
        !          1875:       PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name);
        !          1876:       CloseDevice(atadev, name);
        !          1877:       return 2;
        !          1878:     }
        !          1879:   }
        !          1880:   
        !          1881:   if (ataEnableSmart(atadev)) {
        !          1882:     // Enable SMART command has failed
        !          1883:     PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
        !          1884:     CloseDevice(atadev, name);
        !          1885:     return 2; 
        !          1886:   }
        !          1887:   
        !          1888:   // disable device attribute autosave...
        !          1889:   if (cfg.autosave==1) {
        !          1890:     if (ataDisableAutoSave(atadev))
        !          1891:       PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name);
        !          1892:     else
        !          1893:       PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name);
        !          1894:   }
        !          1895: 
        !          1896:   // or enable device attribute autosave
        !          1897:   if (cfg.autosave==2) {
        !          1898:     if (ataEnableAutoSave(atadev))
        !          1899:       PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name);
        !          1900:     else
        !          1901:       PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name);
        !          1902:   }
        !          1903: 
        !          1904:   // capability check: SMART status
        !          1905:   if (cfg.smartcheck && ataSmartStatus2(atadev) == -1) {
        !          1906:     PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
        !          1907:     cfg.smartcheck = false;
        !          1908:   }
        !          1909:   
        !          1910:   // capability check: Read smart values and thresholds.  Note that
        !          1911:   // smart values are ALSO needed even if we ONLY want to know if the
        !          1912:   // device is self-test log or error-log capable!  After ATA-5, this
        !          1913:   // information was ALSO reproduced in the IDENTIFY DEVICE response,
        !          1914:   // but sadly not for ATA-5.  Sigh.
        !          1915: 
        !          1916:   // do we need to get SMART data?
        !          1917:   bool smart_val_ok = false;
        !          1918:   if (   cfg.autoofflinetest || cfg.selftest
        !          1919:       || cfg.errorlog        || cfg.xerrorlog
        !          1920:       || cfg.offlinests      || cfg.selfteststs
        !          1921:       || cfg.usagefailed     || cfg.prefail  || cfg.usage
        !          1922:       || cfg.tempdiff        || cfg.tempinfo || cfg.tempcrit
        !          1923:       || cfg.curr_pending_id || cfg.offl_pending_id         ) {
        !          1924: 
        !          1925:     if (ataReadSmartValues(atadev, &state.smartval)) {
        !          1926:       PrintOut(LOG_INFO, "Device: %s, Read SMART Values failed\n", name);
        !          1927:       cfg.usagefailed = cfg.prefail = cfg.usage = false;
        !          1928:       cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
        !          1929:       cfg.curr_pending_id = cfg.offl_pending_id = 0;
        !          1930:     }
        !          1931:     else {
        !          1932:       smart_val_ok = true;
        !          1933:       if (ataReadSmartThresholds(atadev, &state.smartthres)) {
        !          1934:         PrintOut(LOG_INFO, "Device: %s, Read SMART Thresholds failed%s\n",
        !          1935:                  name, (cfg.usagefailed ? ", ignoring -f Directive" : ""));
        !          1936:         cfg.usagefailed = false;
        !          1937:         // Let ata_get_attr_state() return ATTRSTATE_NO_THRESHOLD:
        !          1938:         memset(&state.smartthres, 0, sizeof(state.smartthres));
        !          1939:       }
        !          1940:     }
        !          1941: 
        !          1942:     // see if the necessary Attribute is there to monitor offline or
        !          1943:     // current pending sectors or temperature
        !          1944:     if (   cfg.curr_pending_id
        !          1945:         && !check_pending_id(cfg, state, cfg.curr_pending_id,
        !          1946:               "Current_Pending_Sector"))
        !          1947:       cfg.curr_pending_id = 0;
        !          1948: 
        !          1949:     if (   cfg.offl_pending_id
        !          1950:         && !check_pending_id(cfg, state, cfg.offl_pending_id,
        !          1951:               "Offline_Uncorrectable"))
        !          1952:       cfg.offl_pending_id = 0;
        !          1953: 
        !          1954:     if (   (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
        !          1955:         && !ata_return_temperature_value(&state.smartval, cfg.attribute_defs)) {
        !          1956:       PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", name);
        !          1957:       cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
        !          1958:     }
        !          1959:   }
        !          1960:   
        !          1961:   // enable/disable automatic on-line testing
        !          1962:   if (cfg.autoofflinetest) {
        !          1963:     // is this an enable or disable request?
        !          1964:     const char *what=(cfg.autoofflinetest==1)?"disable":"enable";
        !          1965:     if (!smart_val_ok)
        !          1966:       PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what);
        !          1967:     else {
        !          1968:       // if command appears unsupported, issue a warning...
        !          1969:       if (!isSupportAutomaticTimer(&state.smartval))
        !          1970:         PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name);
        !          1971:       // ... but then try anyway
        !          1972:       if ((cfg.autoofflinetest==1)?ataDisableAutoOffline(atadev):ataEnableAutoOffline(atadev))
        !          1973:         PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what);
        !          1974:       else
        !          1975:         PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what);
        !          1976:     }
        !          1977:   }
        !          1978: 
        !          1979:   // Read log directories if required for capability check
        !          1980:   ata_smart_log_directory smart_logdir, gp_logdir;
        !          1981:   bool smart_logdir_ok = false, gp_logdir_ok = false;
        !          1982: 
        !          1983:   if (   isGeneralPurposeLoggingCapable(&drive)
        !          1984:       && (cfg.errorlog || cfg.selftest)        ) {
        !          1985:       if (!ataReadLogDirectory(atadev, &smart_logdir, false))
        !          1986:         smart_logdir_ok = true;
        !          1987:   }
        !          1988: 
        !          1989:   if (cfg.xerrorlog) {
        !          1990:     if (!ataReadLogDirectory(atadev, &gp_logdir, true))
        !          1991:       gp_logdir_ok = true;
        !          1992:   }
        !          1993: 
        !          1994:   // capability check: self-test-log
        !          1995:   state.selflogcount = 0; state.selfloghour = 0;
        !          1996:   if (cfg.selftest) {
        !          1997:     int retval;
        !          1998:     if (!(   cfg.permissive
        !          1999:           || ( smart_logdir_ok && smart_logdir.entry[0x06-1].numsectors)
        !          2000:           || (!smart_logdir_ok && smart_val_ok && isSmartTestLogCapable(&state.smartval, &drive)))) {
        !          2001:       PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest (override with -T permissive)\n", name);
        !          2002:       cfg.selftest = false;
        !          2003:     }
        !          2004:     else if ((retval = SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug)) < 0) {
        !          2005:       PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest\n", name);
        !          2006:       cfg.selftest = false;
        !          2007:     }
        !          2008:     else {
        !          2009:       state.selflogcount=SELFTEST_ERRORCOUNT(retval);
        !          2010:       state.selfloghour =SELFTEST_ERRORHOURS(retval);
        !          2011:     }
        !          2012:   }
        !          2013:   
        !          2014:   // capability check: ATA error log
        !          2015:   state.ataerrorcount = 0;
        !          2016:   if (cfg.errorlog) {
        !          2017:     int errcnt1;
        !          2018:     if (!(   cfg.permissive
        !          2019:           || ( smart_logdir_ok && smart_logdir.entry[0x01-1].numsectors)
        !          2020:           || (!smart_logdir_ok && smart_val_ok && isSmartErrorLogCapable(&state.smartval, &drive)))) {
        !          2021:       PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error (override with -T permissive)\n", name);
        !          2022:       cfg.errorlog = false;
        !          2023:     }
        !          2024:     else if ((errcnt1 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, false)) < 0) {
        !          2025:       PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error\n", name);
        !          2026:       cfg.errorlog = false;
        !          2027:     }
        !          2028:     else
        !          2029:       state.ataerrorcount = errcnt1;
        !          2030:   }
        !          2031: 
        !          2032:   if (cfg.xerrorlog) {
        !          2033:     int errcnt2;
        !          2034:     if (!(cfg.permissive || (gp_logdir_ok && gp_logdir.entry[0x03-1].numsectors))) {
        !          2035:       PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror (override with -T permissive)\n",
        !          2036:                name);
        !          2037:       cfg.xerrorlog = false;
        !          2038:     }
        !          2039:     else if ((errcnt2 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, true)) < 0) {
        !          2040:       PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror\n", name);
        !          2041:       cfg.xerrorlog = false;
        !          2042:     }
        !          2043:     else if (cfg.errorlog && state.ataerrorcount != errcnt2) {
        !          2044:       PrintOut(LOG_INFO, "Device: %s, SMART Error Logs report different error counts: %d != %d\n",
        !          2045:                name, state.ataerrorcount, errcnt2);
        !          2046:       // Record max error count
        !          2047:       if (errcnt2 > state.ataerrorcount)
        !          2048:         state.ataerrorcount = errcnt2;
        !          2049:     }
        !          2050:     else
        !          2051:       state.ataerrorcount = errcnt2;
        !          2052:   }
        !          2053: 
        !          2054:   // capability check: self-test and offline data collection status
        !          2055:   if (cfg.offlinests || cfg.selfteststs) {
        !          2056:     if (!(cfg.permissive || (smart_val_ok && state.smartval.offline_data_collection_capability))) {
        !          2057:       if (cfg.offlinests)
        !          2058:         PrintOut(LOG_INFO, "Device: %s, no SMART Offline Data Collection capability, ignoring -l offlinests (override with -T permissive)\n", name);
        !          2059:       if (cfg.selfteststs)
        !          2060:         PrintOut(LOG_INFO, "Device: %s, no SMART Self-test capability, ignoring -l selfteststs (override with -T permissive)\n", name);
        !          2061:       cfg.offlinests = cfg.selfteststs = false;
        !          2062:     }
        !          2063:   }
        !          2064: 
        !          2065:   // capabilities check -- does it support powermode?
        !          2066:   if (cfg.powermode) {
        !          2067:     int powermode = ataCheckPowerMode(atadev);
        !          2068:     
        !          2069:     if (-1 == powermode) {
        !          2070:       PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
        !          2071:       cfg.powermode=0;
        !          2072:     } 
        !          2073:     else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) {
        !          2074:       PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
        !          2075:               name, powermode);
        !          2076:       cfg.powermode=0;
        !          2077:     }
        !          2078:   }
        !          2079: 
        !          2080:   // set SCT Error Recovery Control if requested
        !          2081:   if (cfg.sct_erc_set) {
        !          2082:     if (!isSCTErrorRecoveryControlCapable(&drive))
        !          2083:       PrintOut(LOG_INFO, "Device: %s, no SCT Error Recovery Control support, ignoring -l scterc\n",
        !          2084:                name);
        !          2085:     else if (   ataSetSCTErrorRecoveryControltime(atadev, 1, cfg.sct_erc_readtime )
        !          2086:              || ataSetSCTErrorRecoveryControltime(atadev, 2, cfg.sct_erc_writetime))
        !          2087:       PrintOut(LOG_INFO, "Device: %s, set of SCT Error Recovery Control failed\n", name);
        !          2088:     else
        !          2089:       PrintOut(LOG_INFO, "Device: %s, SCT Error Recovery Control set to: Read: %u, Write: %u\n",
        !          2090:                name, cfg.sct_erc_readtime, cfg.sct_erc_writetime);
        !          2091:   }
        !          2092: 
        !          2093:   // If no tests available or selected, return
        !          2094:   if (!(   cfg.smartcheck  || cfg.selftest
        !          2095:         || cfg.errorlog    || cfg.xerrorlog
        !          2096:         || cfg.offlinests  || cfg.selfteststs
        !          2097:         || cfg.usagefailed || cfg.prefail  || cfg.usage
        !          2098:         || cfg.tempdiff    || cfg.tempinfo || cfg.tempcrit)) {
        !          2099:     CloseDevice(atadev, name);
        !          2100:     return 3;
        !          2101:   }
        !          2102:   
        !          2103:   // tell user we are registering device
        !          2104:   PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name);
        !          2105:   
        !          2106:   // close file descriptor
        !          2107:   CloseDevice(atadev, name);
        !          2108: 
        !          2109:   if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) {
        !          2110:     // Build file name for state file
        !          2111:     std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_');
        !          2112:     std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_');
        !          2113:     if (!state_path_prefix.empty()) {
        !          2114:       cfg.state_file = strprintf("%s%s-%s.ata.state", state_path_prefix.c_str(), model, serial);
        !          2115:       // Read previous state
        !          2116:       if (read_dev_state(cfg.state_file.c_str(), state)) {
        !          2117:         PrintOut(LOG_INFO, "Device: %s, state read from %s\n", name, cfg.state_file.c_str());
        !          2118:         // Copy ATA attribute values to temp state
        !          2119:         state.update_temp_state();
        !          2120:       }
        !          2121:     }
        !          2122:     if (!attrlog_path_prefix.empty())
        !          2123:       cfg.attrlog_file = strprintf("%s%s-%s.ata.csv", attrlog_path_prefix.c_str(), model, serial);
        !          2124:   }
        !          2125: 
        !          2126:   finish_device_scan(cfg, state);
        !          2127: 
        !          2128:   return 0;
        !          2129: }
        !          2130: 
        !          2131: // on success, return 0. On failure, return >0.  Never return <0,
        !          2132: // please.
        !          2133: static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scsidev)
        !          2134: {
        !          2135:   int k, err, req_len, avail_len, version, len;
        !          2136:   const char *device = cfg.name.c_str();
        !          2137:   struct scsi_iec_mode_page iec;
        !          2138:   UINT8  tBuf[64];
        !          2139:   UINT8  inqBuf[96];
        !          2140:   UINT8  vpdBuf[252];
        !          2141:   char lu_id[64];
        !          2142: 
        !          2143:   // Device must be open
        !          2144:   memset(inqBuf, 0, 96);
        !          2145:   req_len = 36;
        !          2146:   if ((err = scsiStdInquiry(scsidev, inqBuf, req_len))) {
        !          2147:     /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
        !          2148:     req_len = 64;
        !          2149:     if ((err = scsiStdInquiry(scsidev, inqBuf, req_len))) {
        !          2150:       PrintOut(LOG_INFO, "Device: %s, Both 36 and 64 byte INQUIRY failed; "
        !          2151:               "skip device\n", device);
        !          2152:       return 2;
        !          2153:     }
        !          2154:   }
        !          2155:   version = inqBuf[2];
        !          2156:   avail_len = inqBuf[4] + 5;
        !          2157:   len = (avail_len < req_len) ? avail_len : req_len;
        !          2158:   // peri_dt = inqBuf[0] & 0x1f;
        !          2159:   if (len < 36) {
        !          2160:     PrintOut(LOG_INFO, "Device: %s, INQUIRY response less than 36 bytes; "
        !          2161:             "skip device\n", device);
        !          2162:     return 2;
        !          2163:   }
        !          2164:   lu_id[0] = '\0';
        !          2165:   if ((version >= 0x4) && (version < 0x8)) {
        !          2166:     /* SPC-2 to SPC-5 */
        !          2167:     if (0 == (err = scsiInquiryVpd(scsidev, 0x83, vpdBuf, sizeof(vpdBuf)))) {
        !          2168:       len = vpdBuf[3];
        !          2169:       scsi_decode_lu_dev_id(vpdBuf + 4, len, lu_id, sizeof(lu_id), NULL);
        !          2170:     }
        !          2171:   } 
        !          2172: 
        !          2173:   unsigned int lb_size;
        !          2174:   char si_str[64];
        !          2175:   uint64_t capacity = scsiGetSize(scsidev, &lb_size);
        !          2176: 
        !          2177:   if (capacity)
        !          2178:     format_capacity(si_str, sizeof(si_str), capacity);
        !          2179:   else
        !          2180:     si_str[0] = '\0';
        !          2181:   PrintOut(LOG_INFO, "Device: %s, [%.8s %.16s %.4s]%s%s%s%s\n",
        !          2182:           device, (char *)&inqBuf[8], (char *)&inqBuf[16],
        !          2183:           (char *)&inqBuf[32],
        !          2184:           (lu_id[0] ? ", lu id: " : ""), (lu_id[0] ? lu_id : ""),
        !          2185:           (si_str[0] ? ", " : ""), (si_str[0] ? si_str : ""));
        !          2186: 
        !          2187:   // check that device is ready for commands. IE stores its stuff on
        !          2188:   // the media.
        !          2189:   if ((err = scsiTestUnitReady(scsidev))) {
        !          2190:     if (SIMPLE_ERR_NOT_READY == err)
        !          2191:       PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device);
        !          2192:     else if (SIMPLE_ERR_NO_MEDIUM == err)
        !          2193:       PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device);
        !          2194:     else if (SIMPLE_ERR_BECOMING_READY == err)
        !          2195:       PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device);
        !          2196:     else
        !          2197:       PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err);
        !          2198:     CloseDevice(scsidev, device);
        !          2199:     return 2; 
        !          2200:   }
        !          2201:   
        !          2202:   // Badly-conforming USB storage devices may fail this check.
        !          2203:   // The response to the following IE mode page fetch (current and
        !          2204:   // changeable values) is carefully examined. It has been found
        !          2205:   // that various USB devices that malform the response will lock up
        !          2206:   // if asked for a log page (e.g. temperature) so it is best to
        !          2207:   // bail out now.
        !          2208:   if (!(err = scsiFetchIECmpage(scsidev, &iec, state.modese_len)))
        !          2209:     state.modese_len = iec.modese_len;
        !          2210:   else if (SIMPLE_ERR_BAD_FIELD == err)
        !          2211:     ;  /* continue since it is reasonable not to support IE mpage */
        !          2212:   else { /* any other error (including malformed response) unreasonable */
        !          2213:     PrintOut(LOG_INFO, 
        !          2214:              "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", 
        !          2215:              device, err);
        !          2216:     CloseDevice(scsidev, device);
        !          2217:     return 3;
        !          2218:   }
        !          2219:   
        !          2220:   // N.B. The following is passive (i.e. it doesn't attempt to turn on
        !          2221:   // smart if it is off). This may change to be the same as the ATA side.
        !          2222:   if (!scsi_IsExceptionControlEnabled(&iec)) {
        !          2223:     PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n"
        !          2224:                       "Try 'smartctl -s on %s' to turn on SMART features\n", 
        !          2225:                         device, device);
        !          2226:     CloseDevice(scsidev, device);
        !          2227:     return 3;
        !          2228:   }
        !          2229:   
        !          2230:   // Flag that certain log pages are supported (information may be
        !          2231:   // available from other sources).
        !          2232:   if (0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
        !          2233:     for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
        !          2234:       switch (tBuf[k]) { 
        !          2235:       case TEMPERATURE_LPAGE:
        !          2236:         state.TempPageSupported = 1;
        !          2237:         break;
        !          2238:       case IE_LPAGE:
        !          2239:         state.SmartPageSupported = 1;
        !          2240:         break;
        !          2241:       default:
        !          2242:         break;
        !          2243:       }
        !          2244:     }   
        !          2245:   }
        !          2246:   
        !          2247:   // Check if scsiCheckIE() is going to work
        !          2248:   {
        !          2249:     UINT8 asc = 0;
        !          2250:     UINT8 ascq = 0;
        !          2251:     UINT8 currenttemp = 0;
        !          2252:     UINT8 triptemp = 0;
        !          2253:     
        !          2254:     if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported,
        !          2255:                     &asc, &ascq, &currenttemp, &triptemp)) {
        !          2256:       PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device);
        !          2257:       state.SuppressReport = 1;
        !          2258:       if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) {
        !          2259:         PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", device);
        !          2260:         cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
        !          2261:       }
        !          2262:     }
        !          2263:   }
        !          2264:   
        !          2265:   // capability check: self-test-log
        !          2266:   if (cfg.selftest){
        !          2267:     int retval = scsiCountFailedSelfTests(scsidev, 0);
        !          2268:     if (retval<0) {
        !          2269:       // no self-test log, turn off monitoring
        !          2270:       PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device);
        !          2271:       cfg.selftest = false;
        !          2272:       state.selflogcount = 0;
        !          2273:       state.selfloghour = 0;
        !          2274:     }
        !          2275:     else {
        !          2276:       // register starting values to watch for changes
        !          2277:       state.selflogcount=SELFTEST_ERRORCOUNT(retval);
        !          2278:       state.selfloghour =SELFTEST_ERRORHOURS(retval);
        !          2279:     }
        !          2280:   }
        !          2281:   
        !          2282:   // disable autosave (set GLTSD bit)
        !          2283:   if (cfg.autosave==1){
        !          2284:     if (scsiSetControlGLTSD(scsidev, 1, state.modese_len))
        !          2285:       PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device);
        !          2286:     else
        !          2287:       PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device);
        !          2288:   }
        !          2289: 
        !          2290:   // or enable autosave (clear GLTSD bit)
        !          2291:   if (cfg.autosave==2){
        !          2292:     if (scsiSetControlGLTSD(scsidev, 0, state.modese_len))
        !          2293:       PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device);
        !          2294:     else
        !          2295:       PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device);
        !          2296:   }
        !          2297:   
        !          2298:   // tell user we are registering device
        !          2299:   PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device);
        !          2300: 
        !          2301:   // TODO: Build file name for state file
        !          2302:   if (!state_path_prefix.empty()) {
        !          2303:     PrintOut(LOG_INFO, "Device: %s, persistence not yet supported for SCSI; ignoring -s option.\n", device);
        !          2304:   }
        !          2305:   // TODO: Build file name for attribute log file
        !          2306:   if (!attrlog_path_prefix.empty()) {
        !          2307:     PrintOut(LOG_INFO, "Device: %s, attribute log not yet supported for SCSI; ignoring -A option.\n", device);
        !          2308:   }
        !          2309: 
        !          2310:   // close file descriptor
        !          2311:   CloseDevice(scsidev, device);
        !          2312: 
        !          2313:   finish_device_scan(cfg, state);
        !          2314: 
        !          2315:   return 0;
        !          2316: }
        !          2317: 
        !          2318: // If the self-test log has got more self-test errors (or more recent
        !          2319: // self-test errors) recorded, then notify user.
        !          2320: static void CheckSelfTestLogs(const dev_config & cfg, dev_state & state, int newi)
        !          2321: {
        !          2322:   const char * name = cfg.name.c_str();
        !          2323: 
        !          2324:   if (newi<0)
        !          2325:     // command failed
        !          2326:     MailWarning(cfg, state, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
        !          2327:   else {
        !          2328:     reset_warning_mail(cfg, state, 8, "Read SMART Self-Test Log worked again");
        !          2329: 
        !          2330:     // old and new error counts
        !          2331:     int oldc=state.selflogcount;
        !          2332:     int newc=SELFTEST_ERRORCOUNT(newi);
        !          2333:     
        !          2334:     // old and new error timestamps in hours
        !          2335:     int oldh=state.selfloghour;
        !          2336:     int newh=SELFTEST_ERRORHOURS(newi);
        !          2337:     
        !          2338:     if (oldc<newc) {
        !          2339:       // increase in error count
        !          2340:       PrintOut(LOG_CRIT, "Device: %s, Self-Test Log error count increased from %d to %d\n",
        !          2341:                name, oldc, newc);
        !          2342:       MailWarning(cfg, state, 3, "Device: %s, Self-Test Log error count increased from %d to %d",
        !          2343:                    name, oldc, newc);
        !          2344:       state.must_write = true;
        !          2345:     }
        !          2346:     else if (newc > 0 && oldh != newh) {
        !          2347:       // more recent error
        !          2348:       // a 'more recent' error might actually be a smaller hour number,
        !          2349:       // if the hour number has wrapped.
        !          2350:       // There's still a bug here.  You might just happen to run a new test
        !          2351:       // exactly 32768 hours after the previous failure, and have run exactly
        !          2352:       // 20 tests between the two, in which case smartd will miss the
        !          2353:       // new failure.
        !          2354:       PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
        !          2355:                name, newh);
        !          2356:       MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
        !          2357:                    name, newh);
        !          2358:       state.must_write = true;
        !          2359:     }
        !          2360: 
        !          2361:     // Print info if error entries have disappeared
        !          2362:     // or newer successful successful extended self-test exits
        !          2363:     if (oldc > newc) {
        !          2364:       PrintOut(LOG_INFO, "Device: %s, Self-Test Log error count decreased from %d to %d\n",
        !          2365:                name, oldc, newc);
        !          2366:       if (newc == 0)
        !          2367:         reset_warning_mail(cfg, state, 3, "Self-Test Log does no longer report errors");
        !          2368:     }
        !          2369: 
        !          2370:     // Needed since self-test error count may DECREASE.  Hour might
        !          2371:     // also have changed.
        !          2372:     state.selflogcount= newc;
        !          2373:     state.selfloghour = newh;
        !          2374:   }
        !          2375:   return;
        !          2376: }
        !          2377: 
        !          2378: // Test types, ordered by priority.
        !          2379: static const char test_type_chars[] = "LncrSCO";
        !          2380: static const unsigned num_test_types = sizeof(test_type_chars)-1;
        !          2381: 
        !          2382: // returns test type if time to do test of type testtype,
        !          2383: // 0 if not time to do test.
        !          2384: static char next_scheduled_test(const dev_config & cfg, dev_state & state, bool scsi, time_t usetime = 0)
        !          2385: {
        !          2386:   // check that self-testing has been requested
        !          2387:   if (cfg.test_regex.empty())
        !          2388:     return 0;
        !          2389: 
        !          2390:   // Exit if drive not capable of any test
        !          2391:   if ( state.not_cap_long && state.not_cap_short &&
        !          2392:       (scsi || (state.not_cap_conveyance && state.not_cap_offline)))
        !          2393:     return 0;
        !          2394: 
        !          2395:   // since we are about to call localtime(), be sure glibc is informed
        !          2396:   // of any timezone changes we make.
        !          2397:   if (!usetime)
        !          2398:     FixGlibcTimeZoneBug();
        !          2399:   
        !          2400:   // Is it time for next check?
        !          2401:   time_t now = (!usetime ? time(0) : usetime);
        !          2402:   if (now < state.scheduled_test_next_check)
        !          2403:     return 0;
        !          2404: 
        !          2405:   // Limit time check interval to 90 days
        !          2406:   if (state.scheduled_test_next_check + (3600L*24*90) < now)
        !          2407:     state.scheduled_test_next_check = now - (3600L*24*90);
        !          2408: 
        !          2409:   // Check interval [state.scheduled_test_next_check, now] for scheduled tests
        !          2410:   char testtype = 0;
        !          2411:   time_t testtime = 0; int testhour = 0;
        !          2412:   int maxtest = num_test_types-1;
        !          2413: 
        !          2414:   for (time_t t = state.scheduled_test_next_check; ; ) {
        !          2415:     struct tm * tms = localtime(&t);
        !          2416:     // tm_wday is 0 (Sunday) to 6 (Saturday).  We use 1 (Monday) to 7 (Sunday).
        !          2417:     int weekday = (tms->tm_wday ? tms->tm_wday : 7);
        !          2418:     for (int i = 0; i <= maxtest; i++) {
        !          2419:       // Skip if drive not capable of this test
        !          2420:       switch (test_type_chars[i]) {
        !          2421:         case 'L': if (state.not_cap_long)       continue; break;
        !          2422:         case 'S': if (state.not_cap_short)      continue; break;
        !          2423:         case 'C': if (scsi || state.not_cap_conveyance) continue; break;
        !          2424:         case 'O': if (scsi || state.not_cap_offline)    continue; break;
        !          2425:         case 'c': case 'n':
        !          2426:         case 'r': if (scsi || state.not_cap_selective)  continue; break;
        !          2427:         default: continue;
        !          2428:       }
        !          2429:       // Try match of "T/MM/DD/d/HH"
        !          2430:       char pattern[16];
        !          2431:       snprintf(pattern, sizeof(pattern), "%c/%02d/%02d/%1d/%02d",
        !          2432:         test_type_chars[i], tms->tm_mon+1, tms->tm_mday, weekday, tms->tm_hour);
        !          2433:       if (cfg.test_regex.full_match(pattern)) {
        !          2434:         // Test found
        !          2435:         testtype = pattern[0];
        !          2436:         testtime = t; testhour = tms->tm_hour;
        !          2437:         // Limit further matches to higher priority self-tests
        !          2438:         maxtest = i-1;
        !          2439:         break;
        !          2440:       }
        !          2441:     }
        !          2442:     // Exit if no tests left or current time reached
        !          2443:     if (maxtest < 0)
        !          2444:       break;
        !          2445:     if (t >= now)
        !          2446:       break;
        !          2447:     // Check next hour
        !          2448:     if ((t += 3600) > now)
        !          2449:       t = now;
        !          2450:   }
        !          2451:   
        !          2452:   // Do next check not before next hour.
        !          2453:   struct tm * tmnow = localtime(&now);
        !          2454:   state.scheduled_test_next_check = now + (3600 - tmnow->tm_min*60 - tmnow->tm_sec);
        !          2455: 
        !          2456:   if (testtype) {
        !          2457:     state.must_write = true;
        !          2458:     // Tell user if an old test was found.
        !          2459:     if (!usetime && !(testhour == tmnow->tm_hour && testtime + 3600 > now)) {
        !          2460:       char datebuf[DATEANDEPOCHLEN]; dateandtimezoneepoch(datebuf, testtime);
        !          2461:       PrintOut(LOG_INFO, "Device: %s, old test of type %c not run at %s, starting now.\n",
        !          2462:         cfg.name.c_str(), testtype, datebuf);
        !          2463:     }
        !          2464:   }
        !          2465: 
        !          2466:   return testtype;
        !          2467: }
        !          2468: 
        !          2469: // Print a list of future tests.
        !          2470: static void PrintTestSchedule(const dev_config_vector & configs, dev_state_vector & states, const smart_device_list & devices)
        !          2471: {
        !          2472:   unsigned numdev = configs.size();
        !          2473:   if (!numdev)
        !          2474:     return;
        !          2475:   std::vector<int> testcnts(numdev * num_test_types, 0);
        !          2476: 
        !          2477:   PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n");
        !          2478: 
        !          2479:   // FixGlibcTimeZoneBug(); // done in PrintOut()
        !          2480:   time_t now = time(0);
        !          2481:   char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN];
        !          2482:   dateandtimezoneepoch(datenow, now);
        !          2483: 
        !          2484:   long seconds;
        !          2485:   for (seconds=checktime; seconds<3600L*24*90; seconds+=checktime) {
        !          2486:     // Check for each device whether a test will be run
        !          2487:     time_t testtime = now + seconds;
        !          2488:     for (unsigned i = 0; i < numdev; i++) {
        !          2489:       const dev_config & cfg = configs.at(i);
        !          2490:       dev_state & state = states.at(i);
        !          2491:       const char * p;
        !          2492:       char testtype = next_scheduled_test(cfg, state, devices.at(i)->is_scsi(), testtime);
        !          2493:       if (testtype && (p = strchr(test_type_chars, testtype))) {
        !          2494:         unsigned t = (p - test_type_chars);
        !          2495:         // Report at most 5 tests of each type
        !          2496:         if (++testcnts[i*num_test_types + t] <= 5) {
        !          2497:           dateandtimezoneepoch(date, testtime);
        !          2498:           PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg.name.c_str(),
        !          2499:             testcnts[i*num_test_types + t], testtype, date);
        !          2500:         }
        !          2501:       }
        !          2502:     }
        !          2503:   }
        !          2504: 
        !          2505:   // Report totals
        !          2506:   dateandtimezoneepoch(date, now+seconds);
        !          2507:   PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
        !          2508:   for (unsigned i = 0; i < numdev; i++) {
        !          2509:     const dev_config & cfg = configs.at(i);
        !          2510:     bool scsi = devices.at(i)->is_scsi();
        !          2511:     for (unsigned t = 0; t < num_test_types; t++) {
        !          2512:       int cnt = testcnts[i*num_test_types + t];
        !          2513:       if (cnt == 0 && !strchr((scsi ? "LS" : "LSCO"), test_type_chars[t]))
        !          2514:         continue;
        !          2515:       PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg.name.c_str(),
        !          2516:         cnt, (cnt==1?"":"s"), test_type_chars[t]);
        !          2517:     }
        !          2518:   }
        !          2519: 
        !          2520: }
        !          2521: 
        !          2522: // Return zero on success, nonzero on failure. Perform offline (background)
        !          2523: // short or long (extended) self test on given scsi device.
        !          2524: static int DoSCSISelfTest(const dev_config & cfg, dev_state & state, scsi_device * device, char testtype)
        !          2525: {
        !          2526:   int retval = 0;
        !          2527:   const char *testname = 0;
        !          2528:   const char *name = cfg.name.c_str();
        !          2529:   int inProgress;
        !          2530: 
        !          2531:   if (scsiSelfTestInProgress(device, &inProgress)) {
        !          2532:     PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
        !          2533:     state.not_cap_short = state.not_cap_long = true;
        !          2534:     return 1;
        !          2535:   }
        !          2536: 
        !          2537:   if (1 == inProgress) {
        !          2538:     PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in "
        !          2539:              "progress.\n", name);
        !          2540:     return 1;
        !          2541:   }
        !          2542: 
        !          2543:   switch (testtype) {
        !          2544:   case 'S':
        !          2545:     testname = "Short Self";
        !          2546:     retval = scsiSmartShortSelfTest(device);
        !          2547:     break;
        !          2548:   case 'L':
        !          2549:     testname = "Long Self";
        !          2550:     retval = scsiSmartExtendSelfTest(device);
        !          2551:     break;
        !          2552:   }
        !          2553:   // If we can't do the test, exit
        !          2554:   if (NULL == testname) {
        !          2555:     PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name, 
        !          2556:              testtype);
        !          2557:     return 1;
        !          2558:   }
        !          2559:   if (retval) {
        !          2560:     if ((SIMPLE_ERR_BAD_OPCODE == retval) || 
        !          2561:         (SIMPLE_ERR_BAD_FIELD == retval)) {
        !          2562:       PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, 
        !          2563:                testname);
        !          2564:       if ('L'==testtype)
        !          2565:         state.not_cap_long = true;
        !          2566:       else
        !          2567:         state.not_cap_short = true;
        !          2568:      
        !          2569:       return 1;
        !          2570:     }
        !          2571:     PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name, 
        !          2572:              testname, retval);
        !          2573:     return 1;
        !          2574:   }
        !          2575:   
        !          2576:   PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname);
        !          2577:   
        !          2578:   return 0;
        !          2579: }
        !          2580: 
        !          2581: // Do an offline immediate or self-test.  Return zero on success,
        !          2582: // nonzero on failure.
        !          2583: static int DoATASelfTest(const dev_config & cfg, dev_state & state, ata_device * device, char testtype)
        !          2584: {
        !          2585:   const char *name = cfg.name.c_str();
        !          2586: 
        !          2587:   // Read current smart data and check status/capability
        !          2588:   struct ata_smart_values data;
        !          2589:   if (ataReadSmartValues(device, &data) || !(data.offline_data_collection_capability)) {
        !          2590:     PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name);
        !          2591:     return 1;
        !          2592:   }
        !          2593:   
        !          2594:   // Check for capability to do the test
        !          2595:   int dotest = -1, mode = 0;
        !          2596:   const char *testname = 0;
        !          2597:   switch (testtype) {
        !          2598:   case 'O':
        !          2599:     testname="Offline Immediate ";
        !          2600:     if (isSupportExecuteOfflineImmediate(&data))
        !          2601:       dotest=OFFLINE_FULL_SCAN;
        !          2602:     else
        !          2603:       state.not_cap_offline = true;
        !          2604:     break;
        !          2605:   case 'C':
        !          2606:     testname="Conveyance Self-";
        !          2607:     if (isSupportConveyanceSelfTest(&data))
        !          2608:       dotest=CONVEYANCE_SELF_TEST;
        !          2609:     else
        !          2610:       state.not_cap_conveyance = true;
        !          2611:     break;
        !          2612:   case 'S':
        !          2613:     testname="Short Self-";
        !          2614:     if (isSupportSelfTest(&data))
        !          2615:       dotest=SHORT_SELF_TEST;
        !          2616:     else
        !          2617:       state.not_cap_short = true;
        !          2618:     break;
        !          2619:   case 'L':
        !          2620:     testname="Long Self-";
        !          2621:     if (isSupportSelfTest(&data))
        !          2622:       dotest=EXTEND_SELF_TEST;
        !          2623:     else
        !          2624:       state.not_cap_long = true;
        !          2625:     break;
        !          2626: 
        !          2627:   case 'c': case 'n': case 'r':
        !          2628:     testname = "Selective Self-";
        !          2629:     if (isSupportSelectiveSelfTest(&data)) {
        !          2630:       dotest = SELECTIVE_SELF_TEST;
        !          2631:       switch (testtype) {
        !          2632:         case 'c': mode = SEL_CONT; break;
        !          2633:         case 'n': mode = SEL_NEXT; break;
        !          2634:         case 'r': mode = SEL_REDO; break;
        !          2635:       }
        !          2636:     }
        !          2637:     else
        !          2638:       state.not_cap_selective = true;
        !          2639:     break;
        !          2640:   }
        !          2641:   
        !          2642:   // If we can't do the test, exit
        !          2643:   if (dotest<0) {
        !          2644:     PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname);
        !          2645:     return 1;
        !          2646:   }
        !          2647:   
        !          2648:   // If currently running a self-test, do not interrupt it to start another.
        !          2649:   if (15==(data.self_test_exec_status >> 4)) {
        !          2650:     if (cfg.fix_firmwarebug == FIX_SAMSUNG3 && data.self_test_exec_status == 0xf0) {
        !          2651:       PrintOut(LOG_INFO, "Device: %s, will not skip scheduled %sTest "
        !          2652:                "despite unclear Self-Test byte (SAMSUNG Firmware bug).\n", name, testname);
        !          2653:     } else {
        !          2654:       PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n",
        !          2655:                name, testname, (int)(data.self_test_exec_status & 0x0f));
        !          2656:       return 1;
        !          2657:     }
        !          2658:   }
        !          2659: 
        !          2660:   if (dotest == SELECTIVE_SELF_TEST) {
        !          2661:     // Set test span
        !          2662:     ata_selective_selftest_args selargs, prev_args;
        !          2663:     selargs.num_spans = 1;
        !          2664:     selargs.span[0].mode = mode;
        !          2665:     prev_args.num_spans = 1;
        !          2666:     prev_args.span[0].start = state.selective_test_last_start;
        !          2667:     prev_args.span[0].end   = state.selective_test_last_end;
        !          2668:     if (ataWriteSelectiveSelfTestLog(device, selargs, &data, state.num_sectors, &prev_args)) {
        !          2669:       PrintOut(LOG_CRIT, "Device: %s, prepare %sTest failed\n", name, testname);
        !          2670:       return 1;
        !          2671:     }
        !          2672:     uint64_t start = selargs.span[0].start, end = selargs.span[0].end;
        !          2673:     PrintOut(LOG_INFO, "Device: %s, %s test span at LBA %"PRIu64" - %"PRIu64" (%"PRIu64" sectors, %u%% - %u%% of disk).\n",
        !          2674:       name, (selargs.span[0].mode == SEL_NEXT ? "next" : "redo"),
        !          2675:       start, end, end - start + 1,
        !          2676:       (unsigned)((100 * start + state.num_sectors/2) / state.num_sectors),
        !          2677:       (unsigned)((100 * end   + state.num_sectors/2) / state.num_sectors));
        !          2678:     state.selective_test_last_start = start;
        !          2679:     state.selective_test_last_end = end;
        !          2680:   }
        !          2681: 
        !          2682:   // execute the test, and return status
        !          2683:   int retval = smartcommandhandler(device, IMMEDIATE_OFFLINE, dotest, NULL);
        !          2684:   if (retval) {
        !          2685:     PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
        !          2686:     return retval;
        !          2687:   }
        !          2688: 
        !          2689:   if (testtype != 'O')
        !          2690:     // Log next self-test execution status
        !          2691:     state.smartval.self_test_exec_status = 0xff;
        !          2692: 
        !          2693:   PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
        !          2694:   return 0;
        !          2695: }
        !          2696: 
        !          2697: // Check pending sector count attribute values (-C, -U directives).
        !          2698: static void check_pending(const dev_config & cfg, dev_state & state,
        !          2699:                           unsigned char id, bool increase_only,
        !          2700:                           const ata_smart_values & smartval,
        !          2701:                           int mailtype, const char * msg)
        !          2702: {
        !          2703:   // Find attribute index
        !          2704:   int i = ata_find_attr_index(id, smartval);
        !          2705:   if (!(i >= 0 && ata_find_attr_index(id, state.smartval) == i))
        !          2706:     return;
        !          2707: 
        !          2708:   // No report if no sectors pending.
        !          2709:   uint64_t rawval = ata_get_attr_raw_value(smartval.vendor_attributes[i], cfg.attribute_defs);
        !          2710:   if (rawval == 0) {
        !          2711:     reset_warning_mail(cfg, state, mailtype, "No more %s", msg);
        !          2712:     return;
        !          2713:   }
        !          2714: 
        !          2715:   // If attribute is not reset, report only sector count increases.
        !          2716:   uint64_t prev_rawval = ata_get_attr_raw_value(state.smartval.vendor_attributes[i], cfg.attribute_defs);
        !          2717:   if (!(!increase_only || prev_rawval < rawval))
        !          2718:     return;
        !          2719: 
        !          2720:   // Format message.
        !          2721:   std::string s = strprintf("Device: %s, %"PRId64" %s", cfg.name.c_str(), rawval, msg);
        !          2722:   if (prev_rawval > 0 && rawval != prev_rawval)
        !          2723:     s += strprintf(" (changed %+"PRId64")", rawval - prev_rawval);
        !          2724: 
        !          2725:   PrintOut(LOG_CRIT, "%s\n", s.c_str());
        !          2726:   MailWarning(cfg, state, mailtype, "%s\n", s.c_str());
        !          2727:   state.must_write = true;
        !          2728: }
        !          2729: 
        !          2730: // Format Temperature value
        !          2731: static const char * fmt_temp(unsigned char x, char * buf)
        !          2732: {
        !          2733:   if (!x) // unset
        !          2734:     strcpy(buf, "??");
        !          2735:   else
        !          2736:     sprintf(buf, "%u", x);
        !          2737:   return buf;
        !          2738: }
        !          2739: 
        !          2740: // Check Temperature limits
        !          2741: static void CheckTemperature(const dev_config & cfg, dev_state & state, unsigned char currtemp, unsigned char triptemp)
        !          2742: {
        !          2743:   if (!(0 < currtemp && currtemp < 255)) {
        !          2744:     PrintOut(LOG_INFO, "Device: %s, failed to read Temperature\n", cfg.name.c_str());
        !          2745:     return;
        !          2746:   }
        !          2747: 
        !          2748:   // Update Max Temperature
        !          2749:   const char * minchg = "", * maxchg = "";
        !          2750:   if (currtemp > state.tempmax) {
        !          2751:     if (state.tempmax)
        !          2752:       maxchg = "!";
        !          2753:     state.tempmax = currtemp;
        !          2754:     state.must_write = true;
        !          2755:   }
        !          2756: 
        !          2757:   char buf[20];
        !          2758:   if (!state.temperature) {
        !          2759:     // First check
        !          2760:     if (!state.tempmin || currtemp < state.tempmin)
        !          2761:         // Delay Min Temperature update by ~ 30 minutes.
        !          2762:         state.tempmin_delay = time(0) + CHECKTIME - 60;
        !          2763:     PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d Celsius (Min/Max %s/%u%s)\n",
        !          2764:       cfg.name.c_str(), (int)currtemp, fmt_temp(state.tempmin, buf), state.tempmax, maxchg);
        !          2765:     if (triptemp)
        !          2766:       PrintOut(LOG_INFO, "    [trip Temperature is %d Celsius]\n", (int)triptemp);
        !          2767:     state.temperature = currtemp;
        !          2768:   }
        !          2769:   else {
        !          2770:     if (state.tempmin_delay) {
        !          2771:       // End Min Temperature update delay if ...
        !          2772:       if (   (state.tempmin && currtemp > state.tempmin) // current temp exceeds recorded min,
        !          2773:           || (state.tempmin_delay <= time(0))) {         // or delay time is over.
        !          2774:         state.tempmin_delay = 0;
        !          2775:         if (!state.tempmin)
        !          2776:           state.tempmin = 255;
        !          2777:       }
        !          2778:     }
        !          2779: 
        !          2780:     // Update Min Temperature
        !          2781:     if (!state.tempmin_delay && currtemp < state.tempmin) {
        !          2782:       state.tempmin = currtemp;
        !          2783:       state.must_write = true;
        !          2784:       if (currtemp != state.temperature)
        !          2785:         minchg = "!";
        !          2786:     }
        !          2787: 
        !          2788:     // Track changes
        !          2789:     if (cfg.tempdiff && (*minchg || *maxchg || abs((int)currtemp - (int)state.temperature) >= cfg.tempdiff)) {
        !          2790:       PrintOut(LOG_INFO, "Device: %s, Temperature changed %+d Celsius to %u Celsius (Min/Max %s%s/%u%s)\n",
        !          2791:         cfg.name.c_str(), (int)currtemp-(int)state.temperature, currtemp, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
        !          2792:       state.temperature = currtemp;
        !          2793:     }
        !          2794:   }
        !          2795: 
        !          2796:   // Check limits
        !          2797:   if (cfg.tempcrit && currtemp >= cfg.tempcrit) {
        !          2798:     PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n",
        !          2799:       cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
        !          2800:     MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n",
        !          2801:       cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
        !          2802:   }
        !          2803:   else if (cfg.tempinfo && currtemp >= cfg.tempinfo) {
        !          2804:     PrintOut(LOG_INFO, "Device: %s, Temperature %u Celsius reached limit of %u Celsius (Min/Max %s%s/%u%s)\n",
        !          2805:       cfg.name.c_str(), currtemp, cfg.tempinfo, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
        !          2806:   }
        !          2807:   else if (cfg.tempcrit) {
        !          2808:     unsigned char limit = (cfg.tempinfo ? cfg.tempinfo : cfg.tempcrit-5);
        !          2809:     if (currtemp < limit)
        !          2810:       reset_warning_mail(cfg, state, 12, "Temperature %u Celsius dropped below %u Celsius", currtemp, limit);
        !          2811:   }
        !          2812: }
        !          2813: 
        !          2814: // Check normalized and raw attribute values.
        !          2815: static void check_attribute(const dev_config & cfg, dev_state & state,
        !          2816:                             const ata_smart_attribute & attr,
        !          2817:                             const ata_smart_attribute & prev,
        !          2818:                             int attridx,
        !          2819:                             const ata_smart_threshold_entry * thresholds)
        !          2820: {
        !          2821:   // Check attribute and threshold
        !          2822:   ata_attr_state attrstate = ata_get_attr_state(attr, attridx, thresholds, cfg.attribute_defs);
        !          2823:   if (attrstate == ATTRSTATE_NON_EXISTING)
        !          2824:     return;
        !          2825: 
        !          2826:   // If requested, check for usage attributes that have failed.
        !          2827:   if (   cfg.usagefailed && attrstate == ATTRSTATE_FAILED_NOW
        !          2828:       && !cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGN_FAILUSE)) {
        !          2829:     std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs);
        !          2830:     PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", cfg.name.c_str(), attr.id, attrname.c_str());
        !          2831:     MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", cfg.name.c_str(), attr.id, attrname.c_str());
        !          2832:     state.must_write = true;
        !          2833:   }
        !          2834: 
        !          2835:   // Return if we're not tracking this type of attribute
        !          2836:   bool prefail = !!ATTRIBUTE_FLAGS_PREFAILURE(attr.flags);
        !          2837:   if (!(   ( prefail && cfg.prefail)
        !          2838:         || (!prefail && cfg.usage  )))
        !          2839:     return;
        !          2840: 
        !          2841:   // Return if '-I ID' was specified
        !          2842:   if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGNORE))
        !          2843:     return;
        !          2844: 
        !          2845:   // Issue warning if they don't have the same ID in all structures.
        !          2846:   if (attr.id != prev.id) {
        !          2847:     PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d\n",
        !          2848:              cfg.name.c_str(), attr.id, prev.id);
        !          2849:     return;
        !          2850:   }
        !          2851: 
        !          2852:   // Compare normalized values if valid.
        !          2853:   bool valchanged = false;
        !          2854:   if (attrstate > ATTRSTATE_NO_NORMVAL) {
        !          2855:     if (attr.current != prev.current)
        !          2856:       valchanged = true;
        !          2857:   }
        !          2858: 
        !          2859:   // Compare raw values if requested.
        !          2860:   bool rawchanged = false;
        !          2861:   if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW)) {
        !          2862:     if (   ata_get_attr_raw_value(attr, cfg.attribute_defs)
        !          2863:         != ata_get_attr_raw_value(prev, cfg.attribute_defs))
        !          2864:       rawchanged = true;
        !          2865:   }
        !          2866: 
        !          2867:   // Return if no change
        !          2868:   if (!(valchanged || rawchanged))
        !          2869:     return;
        !          2870: 
        !          2871:   // Format value strings
        !          2872:   std::string currstr, prevstr;
        !          2873:   if (attrstate == ATTRSTATE_NO_NORMVAL) {
        !          2874:     // Print raw values only
        !          2875:     currstr = strprintf("%s (Raw)",
        !          2876:       ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str());
        !          2877:     prevstr = strprintf("%s (Raw)",
        !          2878:       ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str());
        !          2879:   }
        !          2880:   else if (cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_PRINT)) {
        !          2881:     // Print normalized and raw values
        !          2882:     currstr = strprintf("%d [Raw %s]", attr.current,
        !          2883:       ata_format_attr_raw_value(attr, cfg.attribute_defs).c_str());
        !          2884:     prevstr = strprintf("%d [Raw %s]", prev.current,
        !          2885:       ata_format_attr_raw_value(prev, cfg.attribute_defs).c_str());
        !          2886:   }
        !          2887:   else {
        !          2888:     // Print normalized values only
        !          2889:     currstr = strprintf("%d", attr.current);
        !          2890:     prevstr = strprintf("%d", prev.current);
        !          2891:   }
        !          2892: 
        !          2893:   // Format message
        !          2894:   std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %s to %s",
        !          2895:                               cfg.name.c_str(), (prefail ? "Prefailure" : "Usage"), attr.id,
        !          2896:                               ata_get_smart_attr_name(attr.id, cfg.attribute_defs).c_str(),
        !          2897:                               prevstr.c_str(), currstr.c_str());
        !          2898: 
        !          2899:   // Report this change as critical ?
        !          2900:   if (   (valchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_AS_CRIT))
        !          2901:       || (rawchanged && cfg.monitor_attr_flags.is_set(attr.id, MONITOR_RAW_AS_CRIT))) {
        !          2902:     PrintOut(LOG_CRIT, "%s\n", msg.c_str());
        !          2903:     MailWarning(cfg, state, 2, "%s", msg.c_str());
        !          2904:   }
        !          2905:   else {
        !          2906:     PrintOut(LOG_INFO, "%s\n", msg.c_str());
        !          2907:   }
        !          2908:   state.must_write = true;
        !          2909: }
        !          2910: 
        !          2911: 
        !          2912: static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device * atadev,
        !          2913:                           bool firstpass, bool allow_selftests)
        !          2914: {
        !          2915:   const char * name = cfg.name.c_str();
        !          2916: 
        !          2917:   // If user has asked, test the email warning system
        !          2918:   if (cfg.emailtest)
        !          2919:     MailWarning(cfg, state, 0, "TEST EMAIL from smartd for device: %s", name);
        !          2920: 
        !          2921:   // if we can't open device, fail gracefully rather than hard --
        !          2922:   // perhaps the next time around we'll be able to open it.  ATAPI
        !          2923:   // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
        !          2924:   // given (see linux cdrom driver).
        !          2925:   if (!atadev->open()) {
        !          2926:     PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, atadev->get_errmsg());
        !          2927:     MailWarning(cfg, state, 9, "Device: %s, unable to open device", name);
        !          2928:     return 1;
        !          2929:   }
        !          2930:   if (debugmode)
        !          2931:     PrintOut(LOG_INFO,"Device: %s, opened ATA device\n", name);
        !          2932:   reset_warning_mail(cfg, state, 9, "open device worked again");
        !          2933: 
        !          2934:   // user may have requested (with the -n Directive) to leave the disk
        !          2935:   // alone if it is in idle or sleeping mode.  In this case check the
        !          2936:   // power mode and exit without check if needed
        !          2937:   if (cfg.powermode && !state.powermodefail) {
        !          2938:     int dontcheck=0, powermode=ataCheckPowerMode(atadev);
        !          2939:     const char * mode = 0;
        !          2940:     if (0 <= powermode && powermode < 0xff) {
        !          2941:       // wait for possible spin up and check again
        !          2942:       int powermode2;
        !          2943:       sleep(5);
        !          2944:       powermode2 = ataCheckPowerMode(atadev);
        !          2945:       if (powermode2 > powermode)
        !          2946:         PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
        !          2947:       powermode = powermode2;
        !          2948:     }
        !          2949:         
        !          2950:     switch (powermode){
        !          2951:     case -1:
        !          2952:       // SLEEP
        !          2953:       mode="SLEEP";
        !          2954:       if (cfg.powermode>=1)
        !          2955:         dontcheck=1;
        !          2956:       break;
        !          2957:     case 0:
        !          2958:       // STANDBY
        !          2959:       mode="STANDBY";
        !          2960:       if (cfg.powermode>=2)
        !          2961:         dontcheck=1;
        !          2962:       break;
        !          2963:     case 0x80:
        !          2964:       // IDLE
        !          2965:       mode="IDLE";
        !          2966:       if (cfg.powermode>=3)
        !          2967:         dontcheck=1;
        !          2968:       break;
        !          2969:     case 0xff:
        !          2970:       // ACTIVE/IDLE
        !          2971:       mode="ACTIVE or IDLE";
        !          2972:       break;
        !          2973:     default:
        !          2974:       // UNKNOWN
        !          2975:       PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
        !          2976:         name, powermode);
        !          2977:       state.powermodefail = true;
        !          2978:       break;
        !          2979:     }
        !          2980: 
        !          2981:     // if we are going to skip a check, return now
        !          2982:     if (dontcheck){
        !          2983:       // skip at most powerskipmax checks
        !          2984:       if (!cfg.powerskipmax || state.powerskipcnt<cfg.powerskipmax) {
        !          2985:         CloseDevice(atadev, name);
        !          2986:         if (!state.powerskipcnt && !cfg.powerquiet) // report first only and avoid waking up system disk
        !          2987:           PrintOut(LOG_INFO, "Device: %s, is in %s mode, suspending checks\n", name, mode);
        !          2988:         state.powerskipcnt++;
        !          2989:         return 0;
        !          2990:       }
        !          2991:       else {
        !          2992:         PrintOut(LOG_INFO, "Device: %s, %s mode ignored due to reached limit of skipped checks (%d check%s skipped)\n",
        !          2993:           name, mode, state.powerskipcnt, (state.powerskipcnt==1?"":"s"));
        !          2994:       }
        !          2995:       state.powerskipcnt = 0;
        !          2996:       state.tempmin_delay = time(0) + CHECKTIME - 60; // Delay Min Temperature update
        !          2997:     }
        !          2998:     else if (state.powerskipcnt) {
        !          2999:       PrintOut(LOG_INFO, "Device: %s, is back in %s mode, resuming checks (%d check%s skipped)\n",
        !          3000:         name, mode, state.powerskipcnt, (state.powerskipcnt==1?"":"s"));
        !          3001:       state.powerskipcnt = 0;
        !          3002:       state.tempmin_delay = time(0) + CHECKTIME - 60; // Delay Min Temperature update
        !          3003:     }
        !          3004:   }
        !          3005: 
        !          3006:   // check smart status
        !          3007:   if (cfg.smartcheck) {
        !          3008:     int status=ataSmartStatus2(atadev);
        !          3009:     if (status==-1){
        !          3010:       PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
        !          3011:       MailWarning(cfg, state, 5, "Device: %s, not capable of SMART self-check", name);
        !          3012:       state.must_write = true;
        !          3013:     }
        !          3014:     else if (status==1){
        !          3015:       PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name);
        !          3016:       MailWarning(cfg, state, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name);
        !          3017:       state.must_write = true;
        !          3018:     }
        !          3019:   }
        !          3020:   
        !          3021:   // Check everything that depends upon SMART Data (eg, Attribute values)
        !          3022:   if (   cfg.usagefailed || cfg.prefail || cfg.usage
        !          3023:       || cfg.curr_pending_id || cfg.offl_pending_id
        !          3024:       || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit
        !          3025:       || cfg.selftest ||  cfg.offlinests || cfg.selfteststs) {
        !          3026: 
        !          3027:     // Read current attribute values.
        !          3028:     ata_smart_values curval;
        !          3029:     if (ataReadSmartValues(atadev, &curval)){
        !          3030:       PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name);
        !          3031:       MailWarning(cfg, state, 6, "Device: %s, failed to read SMART Attribute Data", name);
        !          3032:       state.must_write = true;
        !          3033:     }
        !          3034:     else {
        !          3035:       reset_warning_mail(cfg, state, 6, "read SMART Attribute Data worked again");
        !          3036: 
        !          3037:       // look for current or offline pending sectors
        !          3038:       if (cfg.curr_pending_id)
        !          3039:         check_pending(cfg, state, cfg.curr_pending_id, cfg.curr_pending_incr, curval, 10,
        !          3040:                       (!cfg.curr_pending_incr ? "Currently unreadable (pending) sectors"
        !          3041:                                               : "Total unreadable (pending) sectors"    ));
        !          3042: 
        !          3043:       if (cfg.offl_pending_id)
        !          3044:         check_pending(cfg, state, cfg.offl_pending_id, cfg.offl_pending_incr, curval, 11,
        !          3045:                       (!cfg.offl_pending_incr ? "Offline uncorrectable sectors"
        !          3046:                                               : "Total offline uncorrectable sectors"));
        !          3047: 
        !          3048:       // check temperature limits
        !          3049:       if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
        !          3050:         CheckTemperature(cfg, state, ata_return_temperature_value(&curval, cfg.attribute_defs), 0);
        !          3051: 
        !          3052:       // look for failed usage attributes, or track usage or prefail attributes
        !          3053:       if (cfg.usagefailed || cfg.prefail || cfg.usage) {
        !          3054:         for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
        !          3055:           check_attribute(cfg, state,
        !          3056:                           curval.vendor_attributes[i],
        !          3057:                           state.smartval.vendor_attributes[i],
        !          3058:                           i, state.smartthres.thres_entries);
        !          3059:         }
        !          3060:       }
        !          3061: 
        !          3062:       // Log changes of offline data collection status
        !          3063:       if (cfg.offlinests) {
        !          3064:         if (   curval.offline_data_collection_status
        !          3065:                 != state.smartval.offline_data_collection_status
        !          3066:             || (firstpass && (debugmode || (curval.offline_data_collection_status & 0x7d))))
        !          3067:           log_offline_data_coll_status(name, curval.offline_data_collection_status);
        !          3068:       }
        !          3069: 
        !          3070:       // Log changes of self-test execution status
        !          3071:       if (cfg.selfteststs) {
        !          3072:         if (   curval.self_test_exec_status != state.smartval.self_test_exec_status
        !          3073:             || (firstpass && (debugmode || curval.self_test_exec_status != 0x00)))
        !          3074:           log_self_test_exec_status(name, curval.self_test_exec_status);
        !          3075:       }
        !          3076: 
        !          3077:       // Save the new values for the next time around
        !          3078:       state.smartval = curval;
        !          3079:     }
        !          3080:   }
        !          3081:   
        !          3082:   // check if number of selftest errors has increased (note: may also DECREASE)
        !          3083:   if (cfg.selftest)
        !          3084:     CheckSelfTestLogs(cfg, state, SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug));
        !          3085: 
        !          3086:   // check if number of ATA errors has increased
        !          3087:   if (cfg.errorlog || cfg.xerrorlog) {
        !          3088: 
        !          3089:     int errcnt1 = -1, errcnt2 = -1;
        !          3090:     if (cfg.errorlog)
        !          3091:       errcnt1 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, false);
        !          3092:     if (cfg.xerrorlog)
        !          3093:       errcnt2 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, true);
        !          3094: 
        !          3095:     // new number of errors is max of both logs
        !          3096:     int newc = (errcnt1 >= errcnt2 ? errcnt1 : errcnt2);
        !          3097: 
        !          3098:     // did command fail?
        !          3099:     if (newc<0)
        !          3100:       // lack of PrintOut here is INTENTIONAL
        !          3101:       MailWarning(cfg, state, 7, "Device: %s, Read SMART Error Log Failed", name);
        !          3102: 
        !          3103:     // has error count increased?
        !          3104:     int oldc = state.ataerrorcount;
        !          3105:     if (newc>oldc){
        !          3106:       PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n",
        !          3107:                name, oldc, newc);
        !          3108:       MailWarning(cfg, state, 4, "Device: %s, ATA error count increased from %d to %d",
        !          3109:                    name, oldc, newc);
        !          3110:       state.must_write = true;
        !          3111:     }
        !          3112: 
        !          3113:     if (newc>=0)
        !          3114:       state.ataerrorcount=newc;
        !          3115:   }
        !          3116: 
        !          3117:   // if the user has asked, and device is capable (or we're not yet
        !          3118:   // sure) check whether a self test should be done now.
        !          3119:   if (allow_selftests && !cfg.test_regex.empty()) {
        !          3120:     char testtype = next_scheduled_test(cfg, state, false/*!scsi*/);
        !          3121:     if (testtype)
        !          3122:       DoATASelfTest(cfg, state, atadev, testtype);
        !          3123:   }
        !          3124: 
        !          3125:   // Don't leave device open -- the OS/user may want to access it
        !          3126:   // before the next smartd cycle!
        !          3127:   CloseDevice(atadev, name);
        !          3128: 
        !          3129:   // Copy ATA attribute values to persistent state
        !          3130:   state.update_persistent_state();
        !          3131: 
        !          3132:   return 0;
        !          3133: }
        !          3134: 
        !          3135: static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_device * scsidev, bool allow_selftests)
        !          3136: {
        !          3137:     UINT8 asc, ascq;
        !          3138:     UINT8 currenttemp;
        !          3139:     UINT8 triptemp;
        !          3140:     const char * name = cfg.name.c_str();
        !          3141:     const char *cp;
        !          3142: 
        !          3143:     // If the user has asked for it, test the email warning system
        !          3144:     if (cfg.emailtest)
        !          3145:       MailWarning(cfg, state, 0, "TEST EMAIL from smartd for device: %s", name);
        !          3146: 
        !          3147:     // if we can't open device, fail gracefully rather than hard --
        !          3148:     // perhaps the next time around we'll be able to open it
        !          3149:     if (!scsidev->open()) {
        !          3150:       PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, scsidev->get_errmsg());
        !          3151:       MailWarning(cfg, state, 9, "Device: %s, unable to open device", name);
        !          3152:       return 1;
        !          3153:     } else if (debugmode)
        !          3154:         PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name);
        !          3155:     currenttemp = 0;
        !          3156:     asc = 0;
        !          3157:     ascq = 0;
        !          3158:     if (!state.SuppressReport) {
        !          3159:         if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported,
        !          3160:                         &asc, &ascq, &currenttemp, &triptemp)) {
        !          3161:             PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n",
        !          3162:                       name);
        !          3163:             MailWarning(cfg, state, 6, "Device: %s, failed to read SMART values", name);
        !          3164:             state.SuppressReport = 1;
        !          3165:         }
        !          3166:     }
        !          3167:     if (asc > 0) {
        !          3168:         cp = scsiGetIEString(asc, ascq);
        !          3169:         if (cp) {
        !          3170:             PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
        !          3171:             MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp);
        !          3172:         } else if (debugmode)
        !          3173:             PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n",
        !          3174:                      name, (int)asc, (int)ascq);  
        !          3175:     } else if (debugmode)
        !          3176:         PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name);  
        !          3177: 
        !          3178:     // check temperature limits
        !          3179:     if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
        !          3180:       CheckTemperature(cfg, state, currenttemp, triptemp);
        !          3181: 
        !          3182:     // check if number of selftest errors has increased (note: may also DECREASE)
        !          3183:     if (cfg.selftest)
        !          3184:       CheckSelfTestLogs(cfg, state, scsiCountFailedSelfTests(scsidev, 0));
        !          3185:     
        !          3186:     if (allow_selftests && !cfg.test_regex.empty()) {
        !          3187:       char testtype = next_scheduled_test(cfg, state, true/*scsi*/);
        !          3188:       if (testtype)
        !          3189:         DoSCSISelfTest(cfg, state, scsidev, testtype);
        !          3190:     }
        !          3191:     CloseDevice(scsidev, name);
        !          3192:     return 0;
        !          3193: }
        !          3194: 
        !          3195: // Checks the SMART status of all ATA and SCSI devices
        !          3196: static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector & states,
        !          3197:                              smart_device_list & devices, bool firstpass, bool allow_selftests)
        !          3198: {
        !          3199:   for (unsigned i = 0; i < configs.size(); i++) {
        !          3200:     const dev_config & cfg = configs.at(i);
        !          3201:     dev_state & state = states.at(i);
        !          3202:     smart_device * dev = devices.at(i);
        !          3203:     if (dev->is_ata())
        !          3204:       ATACheckDevice(cfg, state, dev->to_ata(), firstpass, allow_selftests);
        !          3205:     else if (dev->is_scsi())
        !          3206:       SCSICheckDevice(cfg, state, dev->to_scsi(), allow_selftests);
        !          3207:   }
        !          3208: }
        !          3209: 
        !          3210: // Set if Initialize() was called
        !          3211: static bool is_initialized = false;
        !          3212: 
        !          3213: // Does initialization right after fork to daemon mode
        !          3214: static void Initialize(time_t *wakeuptime)
        !          3215: {
        !          3216:   // Call Goodbye() on exit
        !          3217:   is_initialized = true;
        !          3218:   
        !          3219:   // write PID file
        !          3220:   if (!debugmode)
        !          3221:     WritePidFile();
        !          3222:   
        !          3223:   // install signal handlers.  On Solaris, can't use signal() because
        !          3224:   // it resets the handler to SIG_DFL after each call.  So use sigset()
        !          3225:   // instead.  So SIGNALFN()==signal() or SIGNALFN()==sigset().
        !          3226:   
        !          3227:   // normal and abnormal exit
        !          3228:   if (SIGNALFN(SIGTERM, sighandler)==SIG_IGN)
        !          3229:     SIGNALFN(SIGTERM, SIG_IGN);
        !          3230:   if (SIGNALFN(SIGQUIT, sighandler)==SIG_IGN)
        !          3231:     SIGNALFN(SIGQUIT, SIG_IGN);
        !          3232:   
        !          3233:   // in debug mode, <CONTROL-C> ==> HUP
        !          3234:   if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN)
        !          3235:     SIGNALFN(SIGINT, SIG_IGN);
        !          3236:   
        !          3237:   // Catch HUP and USR1
        !          3238:   if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN)
        !          3239:     SIGNALFN(SIGHUP, SIG_IGN);
        !          3240:   if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN)
        !          3241:     SIGNALFN(SIGUSR1, SIG_IGN);
        !          3242: #ifdef _WIN32
        !          3243:   if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN)
        !          3244:     SIGNALFN(SIGUSR2, SIG_IGN);
        !          3245: #endif
        !          3246: 
        !          3247:   // initialize wakeup time to CURRENT time
        !          3248:   *wakeuptime=time(NULL);
        !          3249:   
        !          3250:   return;
        !          3251: }
        !          3252: 
        !          3253: #ifdef _WIN32
        !          3254: // Toggle debug mode implemented for native windows only
        !          3255: // (there is no easy way to reopen tty on *nix)
        !          3256: static void ToggleDebugMode()
        !          3257: {
        !          3258:   if (!debugmode) {
        !          3259:     PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n");
        !          3260:     if (!daemon_enable_console("smartd [Debug]")) {
        !          3261:       debugmode = 1;
        !          3262:       daemon_signal(SIGINT, HUPhandler);
        !          3263:       PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid());
        !          3264:     }
        !          3265:     else
        !          3266:       PrintOut(LOG_INFO,"enable console failed\n");
        !          3267:   }
        !          3268:   else if (debugmode == 1) {
        !          3269:     daemon_disable_console();
        !          3270:     debugmode = 0;
        !          3271:     daemon_signal(SIGINT, sighandler);
        !          3272:     PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n");
        !          3273:   }
        !          3274:   else
        !          3275:     PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode);
        !          3276: }
        !          3277: #endif
        !          3278: 
        !          3279: static time_t dosleep(time_t wakeuptime, bool & sigwakeup)
        !          3280: {
        !          3281:   // If past wake-up-time, compute next wake-up-time
        !          3282:   time_t timenow=time(NULL);
        !          3283:   while (wakeuptime<=timenow){
        !          3284:     int intervals=1+(timenow-wakeuptime)/checktime;
        !          3285:     wakeuptime+=intervals*checktime;
        !          3286:   }
        !          3287:   
        !          3288:   // sleep until we catch SIGUSR1 or have completed sleeping
        !          3289:   int addtime = 0;
        !          3290:   while (timenow < wakeuptime+addtime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT) {
        !          3291:     
        !          3292:     // protect user again system clock being adjusted backwards
        !          3293:     if (wakeuptime>timenow+checktime){
        !          3294:       PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n");
        !          3295:       wakeuptime=timenow+checktime;
        !          3296:     }
        !          3297:     
        !          3298:     // Exit sleep when time interval has expired or a signal is received
        !          3299:     sleep(wakeuptime+addtime-timenow);
        !          3300: 
        !          3301: #ifdef _WIN32
        !          3302:     // toggle debug mode?
        !          3303:     if (caughtsigUSR2) {
        !          3304:       ToggleDebugMode();
        !          3305:       caughtsigUSR2 = 0;
        !          3306:     }
        !          3307: #endif
        !          3308: 
        !          3309:     timenow=time(NULL);
        !          3310: 
        !          3311:     // Actual sleep time too long?
        !          3312:     if (!addtime && timenow > wakeuptime+60) {
        !          3313:       if (debugmode)
        !          3314:         PrintOut(LOG_INFO, "Sleep time was %d seconds too long, assuming wakeup from standby mode.\n",
        !          3315:           (int)(timenow-wakeuptime));
        !          3316:       // Wait another 20 seconds to avoid I/O errors during disk spin-up
        !          3317:       addtime = timenow-wakeuptime+20;
        !          3318:       // Use next wake-up-time if close
        !          3319:       int nextcheck = checktime - addtime % checktime;
        !          3320:       if (nextcheck <= 20)
        !          3321:         addtime += nextcheck;
        !          3322:     }
        !          3323:   }
        !          3324:  
        !          3325:   // if we caught a SIGUSR1 then print message and clear signal
        !          3326:   if (caughtsigUSR1){
        !          3327:     PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n",
        !          3328:              wakeuptime-timenow>0?(int)(wakeuptime-timenow):0);
        !          3329:     caughtsigUSR1=0;
        !          3330:     sigwakeup = true;
        !          3331:   }
        !          3332:   
        !          3333:   // return adjusted wakeuptime
        !          3334:   return wakeuptime;
        !          3335: }
        !          3336: 
        !          3337: // Print out a list of valid arguments for the Directive d
        !          3338: static void printoutvaliddirectiveargs(int priority, char d)
        !          3339: {
        !          3340:   switch (d) {
        !          3341:   case 'n':
        !          3342:     PrintOut(priority, "never[,N][,q], sleep[,N][,q], standby[,N][,q], idle[,N][,q]");
        !          3343:     break;
        !          3344:   case 's':
        !          3345:     PrintOut(priority, "valid_regular_expression");
        !          3346:     break;
        !          3347:   case 'd':
        !          3348:     PrintOut(priority, "%s", smi()->get_valid_dev_types_str().c_str());
        !          3349:     break;
        !          3350:   case 'T':
        !          3351:     PrintOut(priority, "normal, permissive");
        !          3352:     break;
        !          3353:   case 'o':
        !          3354:   case 'S':
        !          3355:     PrintOut(priority, "on, off");
        !          3356:     break;
        !          3357:   case 'l':
        !          3358:     PrintOut(priority, "error, selftest");
        !          3359:     break;
        !          3360:   case 'M':
        !          3361:     PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\"");
        !          3362:     break;
        !          3363:   case 'v':
        !          3364:     PrintOut(priority, "\n%s\n", create_vendor_attribute_arg_list().c_str());
        !          3365:     break;
        !          3366:   case 'P':
        !          3367:     PrintOut(priority, "use, ignore, show, showall");
        !          3368:     break;
        !          3369:   case 'F':
        !          3370:     PrintOut(priority, "none, samsung, samsung2, samsung3");
        !          3371:     break;
        !          3372:   }
        !          3373: }
        !          3374: 
        !          3375: // exits with an error message, or returns integer value of token
        !          3376: static int GetInteger(const char *arg, const char *name, const char *token, int lineno, const char *cfgfile,
        !          3377:                int min, int max, char * suffix = 0)
        !          3378: {
        !          3379:   // make sure argument is there
        !          3380:   if (!arg) {
        !          3381:     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n",
        !          3382:              cfgfile, lineno, name, token, min, max);
        !          3383:     return -1;
        !          3384:   }
        !          3385:   
        !          3386:   // get argument value (base 10), check that it's integer, and in-range
        !          3387:   char *endptr;
        !          3388:   int val = strtol(arg,&endptr,10);
        !          3389: 
        !          3390:   // optional suffix present?
        !          3391:   if (suffix) {
        !          3392:     if (!strcmp(endptr, suffix))
        !          3393:       endptr += strlen(suffix);
        !          3394:     else
        !          3395:       *suffix = 0;
        !          3396:   }
        !          3397: 
        !          3398:   if (!(!*endptr && min <= val && val <= max)) {
        !          3399:     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n",
        !          3400:              cfgfile, lineno, name, token, arg, min, max);
        !          3401:     return -1;
        !          3402:   }
        !          3403: 
        !          3404:   // all is well; return value
        !          3405:   return val;
        !          3406: }
        !          3407: 
        !          3408: 
        !          3409: // Get 1-3 small integer(s) for '-W' directive
        !          3410: static int Get3Integers(const char *arg, const char *name, const char *token, int lineno, const char *cfgfile,
        !          3411:                  unsigned char *val1, unsigned char *val2, unsigned char *val3)
        !          3412: {
        !          3413:   unsigned v1 = 0, v2 = 0, v3 = 0;
        !          3414:   int n1 = -1, n2 = -1, n3 = -1, len;
        !          3415:   if (!arg) {
        !          3416:     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes 1-3 integer argument(s) from 0 to 255.\n",
        !          3417:              cfgfile, lineno, name, token);
        !          3418:     return -1;
        !          3419:   }
        !          3420: 
        !          3421:   len = strlen(arg);
        !          3422:   if (!(   sscanf(arg, "%u%n,%u%n,%u%n", &v1, &n1, &v2, &n2, &v3, &n3) >= 1
        !          3423:         && (n1 == len || n2 == len || n3 == len) && v1 <= 255 && v2 <= 255 && v3 <= 255)) {
        !          3424:     PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs 1-3 integer(s) from 0 to 255.\n",
        !          3425:              cfgfile, lineno, name, token, arg);
        !          3426:     return -1;
        !          3427:   }
        !          3428:   *val1 = (unsigned char)v1; *val2 = (unsigned char)v2; *val3 = (unsigned char)v3;
        !          3429:   return 0;
        !          3430: }
        !          3431: 
        !          3432: 
        !          3433: // This function returns 1 if it has correctly parsed one token (and
        !          3434: // any arguments), else zero if no tokens remain.  It returns -1 if an
        !          3435: // error was encountered.
        !          3436: static int ParseToken(char * token, dev_config & cfg)
        !          3437: {
        !          3438:   char sym;
        !          3439:   const char * name = cfg.name.c_str();
        !          3440:   int lineno=cfg.lineno;
        !          3441:   const char *delim = " \n\t";
        !          3442:   int badarg = 0;
        !          3443:   int missingarg = 0;
        !          3444:   const char *arg = 0;
        !          3445: 
        !          3446:   // is the rest of the line a comment
        !          3447:   if (*token=='#')
        !          3448:     return 1;
        !          3449:   
        !          3450:   // is the token not recognized?
        !          3451:   if (*token!='-' || strlen(token)!=2) {
        !          3452:     PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
        !          3453:              configfile, lineno, name, token);
        !          3454:     PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n");
        !          3455:     return -1;
        !          3456:   }
        !          3457:   
        !          3458:   // token we will be parsing:
        !          3459:   sym=token[1];
        !          3460: 
        !          3461:   // parse the token and swallow its argument
        !          3462:   int val;
        !          3463:   char plus[] = "+", excl[] = "!";
        !          3464: 
        !          3465:   switch (sym) {
        !          3466:   case 'C':
        !          3467:     // monitor current pending sector count (default 197)
        !          3468:     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 0)
        !          3469:       return -1;
        !          3470:     cfg.curr_pending_id = (unsigned char)val;
        !          3471:     cfg.curr_pending_incr = (*plus == '+');
        !          3472:     cfg.curr_pending_set = true;
        !          3473:     break;
        !          3474:   case 'U':
        !          3475:     // monitor offline uncorrectable sectors (default 198)
        !          3476:     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 0)
        !          3477:       return -1;
        !          3478:     cfg.offl_pending_id = (unsigned char)val;
        !          3479:     cfg.offl_pending_incr = (*plus == '+');
        !          3480:     cfg.offl_pending_set = true;
        !          3481:     break;
        !          3482:   case 'T':
        !          3483:     // Set tolerance level for SMART command failures
        !          3484:     if ((arg = strtok(NULL, delim)) == NULL) {
        !          3485:       missingarg = 1;
        !          3486:     } else if (!strcmp(arg, "normal")) {
        !          3487:       // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but
        !          3488:       // not on failure of an optional S.M.A.R.T. command.
        !          3489:       // This is the default so we don't need to actually do anything here.
        !          3490:       cfg.permissive = false;
        !          3491:     } else if (!strcmp(arg, "permissive")) {
        !          3492:       // Permissive mode; ignore errors from Mandatory SMART commands
        !          3493:       cfg.permissive = true;
        !          3494:     } else {
        !          3495:       badarg = 1;
        !          3496:     }
        !          3497:     break;
        !          3498:   case 'd':
        !          3499:     // specify the device type
        !          3500:     if ((arg = strtok(NULL, delim)) == NULL) {
        !          3501:       missingarg = 1;
        !          3502:     } else if (!strcmp(arg, "removable")) {
        !          3503:       cfg.removable = true;
        !          3504:     } else if (!strcmp(arg, "auto")) {
        !          3505:       cfg.dev_type = "";
        !          3506:     } else {
        !          3507:       cfg.dev_type = arg;
        !          3508:     }
        !          3509:     break;
        !          3510:   case 'F':
        !          3511:     // fix firmware bug
        !          3512:     if ((arg = strtok(NULL, delim)) == NULL) {
        !          3513:       missingarg = 1;
        !          3514:     } else if (!strcmp(arg, "none")) {
        !          3515:       cfg.fix_firmwarebug = FIX_NONE;
        !          3516:     } else if (!strcmp(arg, "samsung")) {
        !          3517:       cfg.fix_firmwarebug = FIX_SAMSUNG;
        !          3518:     } else if (!strcmp(arg, "samsung2")) {
        !          3519:       cfg.fix_firmwarebug = FIX_SAMSUNG2;
        !          3520:     } else if (!strcmp(arg, "samsung3")) {
        !          3521:       cfg.fix_firmwarebug = FIX_SAMSUNG3;
        !          3522:     } else {
        !          3523:       badarg = 1;
        !          3524:     }
        !          3525:     break;
        !          3526:   case 'H':
        !          3527:     // check SMART status
        !          3528:     cfg.smartcheck = true;
        !          3529:     break;
        !          3530:   case 'f':
        !          3531:     // check for failure of usage attributes
        !          3532:     cfg.usagefailed = true;
        !          3533:     break;
        !          3534:   case 't':
        !          3535:     // track changes in all vendor attributes
        !          3536:     cfg.prefail = true;
        !          3537:     cfg.usage = true;
        !          3538:     break;
        !          3539:   case 'p':
        !          3540:     // track changes in prefail vendor attributes
        !          3541:     cfg.prefail = true;
        !          3542:     break;
        !          3543:   case 'u':
        !          3544:     //  track changes in usage vendor attributes
        !          3545:     cfg.usage = true;
        !          3546:     break;
        !          3547:   case 'l':
        !          3548:     // track changes in SMART logs
        !          3549:     if ((arg = strtok(NULL, delim)) == NULL) {
        !          3550:       missingarg = 1;
        !          3551:     } else if (!strcmp(arg, "selftest")) {
        !          3552:       // track changes in self-test log
        !          3553:       cfg.selftest = true;
        !          3554:     } else if (!strcmp(arg, "error")) {
        !          3555:       // track changes in ATA error log
        !          3556:       cfg.errorlog = true;
        !          3557:     } else if (!strcmp(arg, "xerror")) {
        !          3558:       // track changes in Extended Comprehensive SMART error log
        !          3559:       cfg.xerrorlog = true;
        !          3560:     } else if (!strcmp(arg, "offlinests")) {
        !          3561:       // track changes in offline data collection status
        !          3562:       cfg.offlinests = true;
        !          3563:     } else if (!strcmp(arg, "selfteststs")) {
        !          3564:       // track changes in self-test execution status
        !          3565:       cfg.selfteststs = true;
        !          3566:     } else if (!strncmp(arg, "scterc,", sizeof("scterc,")-1)) {
        !          3567:         // set SCT Error Recovery Control
        !          3568:         unsigned rt = ~0, wt = ~0; int nc = -1;
        !          3569:         sscanf(arg,"scterc,%u,%u%n", &rt, &wt, &nc);
        !          3570:         if (nc == (int)strlen(arg) && rt <= 999 && wt <= 999) {
        !          3571:           cfg.sct_erc_set = true;
        !          3572:           cfg.sct_erc_readtime = rt;
        !          3573:           cfg.sct_erc_writetime = wt;
        !          3574:         }
        !          3575:         else
        !          3576:           badarg = 1;
        !          3577:     } else {
        !          3578:       badarg = 1;
        !          3579:     }
        !          3580:     break;
        !          3581:   case 'a':
        !          3582:     // monitor everything
        !          3583:     cfg.smartcheck = true;
        !          3584:     cfg.prefail = true;
        !          3585:     cfg.usagefailed = true;
        !          3586:     cfg.usage = true;
        !          3587:     cfg.selftest = true;
        !          3588:     cfg.errorlog = true;
        !          3589:     cfg.selfteststs = true;
        !          3590:     break;
        !          3591:   case 'o':
        !          3592:     // automatic offline testing enable/disable
        !          3593:     if ((arg = strtok(NULL, delim)) == NULL) {
        !          3594:       missingarg = 1;
        !          3595:     } else if (!strcmp(arg, "on")) {
        !          3596:       cfg.autoofflinetest = 2;
        !          3597:     } else if (!strcmp(arg, "off")) {
        !          3598:       cfg.autoofflinetest = 1;
        !          3599:     } else {
        !          3600:       badarg = 1;
        !          3601:     }
        !          3602:     break;
        !          3603:   case 'n':
        !          3604:     // skip disk check if in idle or standby mode
        !          3605:     if (!(arg = strtok(NULL, delim)))
        !          3606:       missingarg = 1;
        !          3607:     else {
        !          3608:       char *endptr = NULL;
        !          3609:       char *next = strchr(const_cast<char*>(arg), ',');
        !          3610: 
        !          3611:       cfg.powerquiet = false;
        !          3612:       cfg.powerskipmax = 0;
        !          3613: 
        !          3614:       if (next!=NULL) *next='\0';
        !          3615:       if (!strcmp(arg, "never"))
        !          3616:         cfg.powermode = 0;
        !          3617:       else if (!strcmp(arg, "sleep"))
        !          3618:         cfg.powermode = 1;
        !          3619:       else if (!strcmp(arg, "standby"))
        !          3620:         cfg.powermode = 2;
        !          3621:       else if (!strcmp(arg, "idle"))
        !          3622:         cfg.powermode = 3;
        !          3623:       else
        !          3624:         badarg = 1;
        !          3625: 
        !          3626:       // if optional arguments are present
        !          3627:       if (!badarg && next!=NULL) {
        !          3628:         next++;
        !          3629:         cfg.powerskipmax = strtol(next, &endptr, 10);
        !          3630:         if (endptr == next)
        !          3631:           cfg.powerskipmax = 0;
        !          3632:         else {
        !          3633:           next = endptr + (*endptr != '\0');
        !          3634:           if (cfg.powerskipmax <= 0)
        !          3635:             badarg = 1;
        !          3636:         }
        !          3637:         if (*next != '\0') {
        !          3638:           if (!strcmp("q", next))
        !          3639:             cfg.powerquiet = true;
        !          3640:           else {
        !          3641:             badarg = 1;
        !          3642:           }
        !          3643:         }
        !          3644:       }
        !          3645:     }
        !          3646:     break;
        !          3647:   case 'S':
        !          3648:     // automatic attribute autosave enable/disable
        !          3649:     if ((arg = strtok(NULL, delim)) == NULL) {
        !          3650:       missingarg = 1;
        !          3651:     } else if (!strcmp(arg, "on")) {
        !          3652:       cfg.autosave = 2;
        !          3653:     } else if (!strcmp(arg, "off")) {
        !          3654:       cfg.autosave = 1;
        !          3655:     } else {
        !          3656:       badarg = 1;
        !          3657:     }
        !          3658:     break;
        !          3659:   case 's':
        !          3660:     // warn user, and delete any previously given -s REGEXP Directives
        !          3661:     if (!cfg.test_regex.empty()){
        !          3662:       PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n",
        !          3663:                configfile, lineno, name, cfg.test_regex.get_pattern());
        !          3664:       cfg.test_regex = regular_expression();
        !          3665:     }
        !          3666:     // check for missing argument
        !          3667:     if (!(arg = strtok(NULL, delim))) {
        !          3668:       missingarg = 1;
        !          3669:     }
        !          3670:     // Compile regex
        !          3671:     else {
        !          3672:       if (!cfg.test_regex.compile(arg, REG_EXTENDED)) {
        !          3673:         // not a valid regular expression!
        !          3674:         PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
        !          3675:                  configfile, lineno, name, arg, cfg.test_regex.get_errmsg());
        !          3676:         return -1;
        !          3677:       }
        !          3678:     }
        !          3679:     // Do a bit of sanity checking and warn user if we think that
        !          3680:     // their regexp is "strange". User probably confused about shell
        !          3681:     // glob(3) syntax versus regular expression syntax regexp(7).
        !          3682:     if (arg[(val = strspn(arg, "0123456789/.-+*|()?^$[]SLCOcnr"))])
        !          3683:       PrintOut(LOG_INFO,  "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
        !          3684:                configfile, lineno, name, val+1, arg[val], arg);
        !          3685:     break;
        !          3686:   case 'm':
        !          3687:     // send email to address that follows
        !          3688:     if (!(arg = strtok(NULL,delim)))
        !          3689:       missingarg = 1;
        !          3690:     else {
        !          3691:       if (!cfg.emailaddress.empty())
        !          3692:         PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
        !          3693:                  configfile, lineno, name, cfg.emailaddress.c_str());
        !          3694:       cfg.emailaddress = arg;
        !          3695:     }
        !          3696:     break;
        !          3697:   case 'M':
        !          3698:     // email warning options
        !          3699:     if (!(arg = strtok(NULL, delim)))
        !          3700:       missingarg = 1;
        !          3701:     else if (!strcmp(arg, "once"))
        !          3702:       cfg.emailfreq = 1;
        !          3703:     else if (!strcmp(arg, "daily"))
        !          3704:       cfg.emailfreq = 2;
        !          3705:     else if (!strcmp(arg, "diminishing"))
        !          3706:       cfg.emailfreq = 3;
        !          3707:     else if (!strcmp(arg, "test"))
        !          3708:       cfg.emailtest = 1;
        !          3709:     else if (!strcmp(arg, "exec")) {
        !          3710:       // Get the next argument (the command line)
        !          3711:       if (!(arg = strtok(NULL, delim))) {
        !          3712:         PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n",
        !          3713:                  configfile, lineno, name, token);
        !          3714:         return -1;
        !          3715:       }
        !          3716:       // Free the last cmd line given if any, and copy new one
        !          3717:       if (!cfg.emailcmdline.empty())
        !          3718:         PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n",
        !          3719:                  configfile, lineno, name, cfg.emailcmdline.c_str());
        !          3720:       cfg.emailcmdline = arg;
        !          3721:     } 
        !          3722:     else
        !          3723:       badarg = 1;
        !          3724:     break;
        !          3725:   case 'i':
        !          3726:     // ignore failure of usage attribute
        !          3727:     if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
        !          3728:       return -1;
        !          3729:     cfg.monitor_attr_flags.set(val, MONITOR_IGN_FAILUSE);
        !          3730:     break;
        !          3731:   case 'I':
        !          3732:     // ignore attribute for tracking purposes
        !          3733:     if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
        !          3734:       return -1;
        !          3735:     cfg.monitor_attr_flags.set(val, MONITOR_IGNORE);
        !          3736:     break;
        !          3737:   case 'r':
        !          3738:     // print raw value when tracking
        !          3739:     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255, excl)) < 0)
        !          3740:       return -1;
        !          3741:     cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT);
        !          3742:     if (*excl == '!') // attribute change is critical
        !          3743:       cfg.monitor_attr_flags.set(val, MONITOR_AS_CRIT);
        !          3744:     break;
        !          3745:   case 'R':
        !          3746:     // track changes in raw value (forces printing of raw value)
        !          3747:     if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255, excl)) < 0)
        !          3748:       return -1;
        !          3749:     cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT|MONITOR_RAW);
        !          3750:     if (*excl == '!') // raw value change is critical
        !          3751:       cfg.monitor_attr_flags.set(val, MONITOR_RAW_AS_CRIT);
        !          3752:     break;
        !          3753:   case 'W':
        !          3754:     // track Temperature
        !          3755:     if ((val=Get3Integers(arg=strtok(NULL,delim), name, token, lineno, configfile,
        !          3756:                           &cfg.tempdiff, &cfg.tempinfo, &cfg.tempcrit))<0)
        !          3757:       return -1;
        !          3758:     break;
        !          3759:   case 'v':
        !          3760:     // non-default vendor-specific attribute meaning
        !          3761:     if (!(arg=strtok(NULL,delim))) {
        !          3762:       missingarg = 1;
        !          3763:     } else if (!parse_attribute_def(arg, cfg.attribute_defs, PRIOR_USER)) {
        !          3764:       badarg = 1;
        !          3765:     }
        !          3766:     break;
        !          3767:   case 'P':
        !          3768:     // Define use of drive-specific presets.
        !          3769:     if (!(arg = strtok(NULL, delim))) {
        !          3770:       missingarg = 1;
        !          3771:     } else if (!strcmp(arg, "use")) {
        !          3772:       cfg.ignorepresets = false;
        !          3773:     } else if (!strcmp(arg, "ignore")) {
        !          3774:       cfg.ignorepresets = true;
        !          3775:     } else if (!strcmp(arg, "show")) {
        !          3776:       cfg.showpresets = true;
        !          3777:     } else if (!strcmp(arg, "showall")) {
        !          3778:       showallpresets();
        !          3779:     } else {
        !          3780:       badarg = 1;
        !          3781:     }
        !          3782:     break;
        !          3783:   default:
        !          3784:     // Directive not recognized
        !          3785:     PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
        !          3786:              configfile, lineno, name, token);
        !          3787:     Directives();
        !          3788:     return -1;
        !          3789:   }
        !          3790:   if (missingarg) {
        !          3791:     PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n",
        !          3792:              configfile, lineno, name, token);
        !          3793:   }
        !          3794:   if (badarg) {
        !          3795:     PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n",
        !          3796:              configfile, lineno, name, token, arg);
        !          3797:   }
        !          3798:   if (missingarg || badarg) {
        !          3799:     PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token);
        !          3800:     printoutvaliddirectiveargs(LOG_CRIT, sym);
        !          3801:     PrintOut(LOG_CRIT, "\n");
        !          3802:     return -1;
        !          3803:   }
        !          3804: 
        !          3805:   return 1;
        !          3806: }
        !          3807: 
        !          3808: // Scan directive for configuration file
        !          3809: #define SCANDIRECTIVE "DEVICESCAN"
        !          3810: 
        !          3811: // This is the routine that adds things to the conf_entries list.
        !          3812: //
        !          3813: // Return values are:
        !          3814: //  1: parsed a normal line
        !          3815: //  0: found comment or blank line
        !          3816: // -1: found SCANDIRECTIVE line
        !          3817: // -2: found an error
        !          3818: //
        !          3819: // Note: this routine modifies *line from the caller!
        !          3820: static int ParseConfigLine(dev_config_vector & conf_entries, int /*entry*/, int lineno, /*const*/ char * line)
        !          3821: {
        !          3822:   char *token=NULL;
        !          3823:   char *name=NULL;
        !          3824:   const char *delim = " \n\t";
        !          3825:   int devscan=0;
        !          3826: 
        !          3827:   // get first token: device name. If a comment, skip line
        !          3828:   if (!(name=strtok(line,delim)) || *name=='#') {
        !          3829:     return 0;
        !          3830:   }
        !          3831: 
        !          3832:   // Have we detected the SCANDIRECTIVE directive?
        !          3833:   if (!strcmp(SCANDIRECTIVE,name)){
        !          3834:     devscan=1;
        !          3835:   }
        !          3836:   
        !          3837:   // We've got a legit entry, make space to store it
        !          3838:   conf_entries.push_back( dev_config() );
        !          3839:   dev_config & cfg = conf_entries.back();
        !          3840: 
        !          3841:   cfg.name = name; // Later replaced by dev->get_info().info_name
        !          3842:   cfg.dev_name = name; // If DEVICESCAN later replaced by get->dev_info().dev_name
        !          3843: 
        !          3844:   // Store line number, and by default check for both device types.
        !          3845:   cfg.lineno=lineno;
        !          3846: 
        !          3847:   // parse tokens one at a time from the file.
        !          3848:   while ((token=strtok(NULL,delim))){
        !          3849:     int retval=ParseToken(token,cfg);
        !          3850:     
        !          3851:     if (retval==0)
        !          3852:       // No tokens left:
        !          3853:       break;
        !          3854:     
        !          3855:     if (retval>0) {
        !          3856:       // Parsed token  
        !          3857: #if (0)
        !          3858:       PrintOut(LOG_INFO,"Parsed token %s\n",token);
        !          3859: #endif
        !          3860:       continue;
        !          3861:     }
        !          3862:     
        !          3863:     if (retval<0) {
        !          3864:       // error found on the line
        !          3865:       return -2;
        !          3866:     }
        !          3867:   }
        !          3868:   
        !          3869:   // If NO monitoring directives are set, then set all of them.
        !          3870:   if (!(   cfg.smartcheck  || cfg.selftest
        !          3871:         || cfg.errorlog    || cfg.xerrorlog
        !          3872:         || cfg.offlinests  || cfg.selfteststs
        !          3873:         || cfg.usagefailed || cfg.prefail  || cfg.usage
        !          3874:         || cfg.tempdiff    || cfg.tempinfo || cfg.tempcrit)) {
        !          3875:     
        !          3876:     PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n",
        !          3877:              cfg.name.c_str(), cfg.lineno, configfile);
        !          3878:     
        !          3879:     cfg.smartcheck = true;
        !          3880:     cfg.usagefailed = true;
        !          3881:     cfg.prefail = true;
        !          3882:     cfg.usage = true;
        !          3883:     cfg.selftest = true;
        !          3884:     cfg.errorlog = true;
        !          3885:     cfg.selfteststs = true;
        !          3886:   }
        !          3887:   
        !          3888:   // additional sanity check. Has user set -M options without -m?
        !          3889:   if (cfg.emailaddress.empty() && (!cfg.emailcmdline.empty() || cfg.emailfreq || cfg.emailtest)){
        !          3890:     PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n",
        !          3891:              cfg.name.c_str(), cfg.lineno, configfile);
        !          3892:     return -2;
        !          3893:   }
        !          3894:   
        !          3895:   // has the user has set <nomailer>?
        !          3896:   if (cfg.emailaddress == "<nomailer>") {
        !          3897:     // check that -M exec is also set
        !          3898:     if (cfg.emailcmdline.empty()){
        !          3899:       PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n",
        !          3900:                cfg.name.c_str(), cfg.lineno, configfile);
        !          3901:       return -2;
        !          3902:     }
        !          3903:     // From here on the sign of <nomailer> is address.empty() and !cfg.emailcmdline.empty()
        !          3904:     cfg.emailaddress.clear();
        !          3905:   }
        !          3906: 
        !          3907:   if (devscan)
        !          3908:     return -1;
        !          3909:   else
        !          3910:     return 1;
        !          3911: }
        !          3912: 
        !          3913: // Parses a configuration file.  Return values are:
        !          3914: //  N=>0: found N entries
        !          3915: // -1:    syntax error in config file
        !          3916: // -2:    config file does not exist
        !          3917: // -3:    config file exists but cannot be read
        !          3918: //
        !          3919: // In the case where the return value is 0, there are three
        !          3920: // possiblities:
        !          3921: // Empty configuration file ==> conf_entries.empty()
        !          3922: // No configuration file    ==> conf_entries[0].lineno == 0
        !          3923: // SCANDIRECTIVE found      ==> conf_entries.back().lineno != 0 (size >= 1)
        !          3924: static int ParseConfigFile(dev_config_vector & conf_entries)
        !          3925: {
        !          3926:   // maximum line length in configuration file
        !          3927:   const int MAXLINELEN = 256;
        !          3928:   // maximum length of a continued line in configuration file
        !          3929:   const int MAXCONTLINE = 1023;
        !          3930: 
        !          3931:   stdio_file f;
        !          3932:   // Open config file, if it exists and is not <stdin>
        !          3933:   if (!(configfile == configfile_stdin)) { // pointer comparison ok here
        !          3934:     if (!f.open(configfile,"r") && (errno!=ENOENT || !configfile_alt.empty())) {
        !          3935:       // file exists but we can't read it or it should exist due to '-c' option
        !          3936:       int ret = (errno!=ENOENT ? -3 : -2);
        !          3937:       PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n",
        !          3938:                strerror(errno),configfile);
        !          3939:       return ret;
        !          3940:     }
        !          3941:   }
        !          3942:   else // read from stdin ('-c -' option)
        !          3943:     f.open(stdin);
        !          3944: 
        !          3945:   // No configuration file found -- use fake one
        !          3946:   int entry = 0;
        !          3947:   if (!f) {
        !          3948:     char fakeconfig[] = SCANDIRECTIVE" -a"; // TODO: Remove this hack, build cfg_entry.
        !          3949: 
        !          3950:     if (ParseConfigLine(conf_entries, entry, 0, fakeconfig) != -1)
        !          3951:       throw std::logic_error("Internal error parsing "SCANDIRECTIVE);
        !          3952:     return 0;
        !          3953:   }
        !          3954: 
        !          3955: #ifdef __CYGWIN__
        !          3956:   setmode(fileno(f), O_TEXT); // Allow files with \r\n
        !          3957: #endif
        !          3958: 
        !          3959:   // configuration file exists
        !          3960:   PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile);
        !          3961: 
        !          3962:   // parse config file line by line
        !          3963:   int lineno = 1, cont = 0, contlineno = 0;
        !          3964:   char line[MAXLINELEN+2];
        !          3965:   char fullline[MAXCONTLINE+1];
        !          3966: 
        !          3967:   for (;;) {
        !          3968:     int len=0,scandevice;
        !          3969:     char *lastslash;
        !          3970:     char *comment;
        !          3971:     char *code;
        !          3972: 
        !          3973:     // make debugging simpler
        !          3974:     memset(line,0,sizeof(line));
        !          3975: 
        !          3976:     // get a line
        !          3977:     code=fgets(line, MAXLINELEN+2, f);
        !          3978:     
        !          3979:     // are we at the end of the file?
        !          3980:     if (!code){
        !          3981:       if (cont) {
        !          3982:         scandevice = ParseConfigLine(conf_entries, entry, contlineno, fullline);
        !          3983:         // See if we found a SCANDIRECTIVE directive
        !          3984:         if (scandevice==-1)
        !          3985:           return 0;
        !          3986:         // did we find a syntax error
        !          3987:         if (scandevice==-2)
        !          3988:           return -1;
        !          3989:         // the final line is part of a continuation line
        !          3990:         cont=0;
        !          3991:         entry+=scandevice;
        !          3992:       }
        !          3993:       break;
        !          3994:     }
        !          3995: 
        !          3996:     // input file line number
        !          3997:     contlineno++;
        !          3998:     
        !          3999:     // See if line is too long
        !          4000:     len=strlen(line);
        !          4001:     if (len>MAXLINELEN){
        !          4002:       const char *warn;
        !          4003:       if (line[len-1]=='\n')
        !          4004:         warn="(including newline!) ";
        !          4005:       else
        !          4006:         warn="";
        !          4007:       PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n",
        !          4008:                (int)contlineno,configfile,warn,(int)MAXLINELEN);
        !          4009:       return -1;
        !          4010:     }
        !          4011: 
        !          4012:     // Ignore anything after comment symbol
        !          4013:     if ((comment=strchr(line,'#'))){
        !          4014:       *comment='\0';
        !          4015:       len=strlen(line);
        !          4016:     }
        !          4017: 
        !          4018:     // is the total line (made of all continuation lines) too long?
        !          4019:     if (cont+len>MAXCONTLINE){
        !          4020:       PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n",
        !          4021:                lineno, (int)contlineno, configfile, (int)MAXCONTLINE);
        !          4022:       return -1;
        !          4023:     }
        !          4024:     
        !          4025:     // copy string so far into fullline, and increment length
        !          4026:     strcpy(fullline+cont,line);
        !          4027:     cont+=len;
        !          4028: 
        !          4029:     // is this a continuation line.  If so, replace \ by space and look at next line
        !          4030:     if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){
        !          4031:       *(fullline+(cont-len)+(lastslash-line))=' ';
        !          4032:       continue;
        !          4033:     }
        !          4034: 
        !          4035:     // Not a continuation line. Parse it
        !          4036:     scandevice = ParseConfigLine(conf_entries, entry, contlineno, fullline);
        !          4037: 
        !          4038:     // did we find a scandevice directive?
        !          4039:     if (scandevice==-1)
        !          4040:       return 0;
        !          4041:     // did we find a syntax error
        !          4042:     if (scandevice==-2)
        !          4043:       return -1;
        !          4044: 
        !          4045:     entry+=scandevice;
        !          4046:     lineno++;
        !          4047:     cont=0;
        !          4048:   }
        !          4049: 
        !          4050:   // note -- may be zero if syntax of file OK, but no valid entries!
        !          4051:   return entry;
        !          4052: }
        !          4053: 
        !          4054: /* Prints the message "=======> VALID ARGUMENTS ARE: <LIST>  <=======\n", where
        !          4055:    <LIST> is the list of valid arguments for option opt. */
        !          4056: static void PrintValidArgs(char opt)
        !          4057: {
        !          4058:   const char *s;
        !          4059: 
        !          4060:   PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: ");
        !          4061:   if (!(s = GetValidArgList(opt)))
        !          4062:     PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt);
        !          4063:   else
        !          4064:     PrintOut(LOG_CRIT, "%s", (char *)s);
        !          4065:   PrintOut(LOG_CRIT, " <=======\n");
        !          4066: }
        !          4067: 
        !          4068: #ifndef _WIN32
        !          4069: // Report error and exit if specified path is not absolute.
        !          4070: static void check_abs_path(char option, const std::string & path)
        !          4071: {
        !          4072:   if (path.empty() || path[0] == '/')
        !          4073:     return;
        !          4074: 
        !          4075:   debugmode = 1;
        !          4076:   PrintHead();
        !          4077:   PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <=======\n\n", option, path.c_str());
        !          4078:   PrintOut(LOG_CRIT, "Error: relative path names are not allowed\n\n");
        !          4079:   EXIT(EXIT_BADCMD);
        !          4080: }
        !          4081: #endif // !_WIN32
        !          4082: 
        !          4083: // Parses input line, prints usage message and
        !          4084: // version/license/copyright messages
        !          4085: static void ParseOpts(int argc, char **argv)
        !          4086: {
        !          4087:   // Init default configfile path
        !          4088: #ifndef _WIN32
        !          4089:   configfile = SMARTMONTOOLS_SYSCONFDIR"/smartd.conf";
        !          4090: #else
        !          4091:   static std::string configfile_str = get_exe_dir() + "/smartd.conf";
        !          4092:   configfile = configfile_str.c_str();
        !          4093: #endif
        !          4094: 
        !          4095:   // Please update GetValidArgList() if you edit shortopts
        !          4096:   static const char shortopts[] = "c:l:q:dDni:p:r:s:A:B:Vh?"
        !          4097: #ifdef HAVE_LIBCAP_NG
        !          4098:                                                           "C"
        !          4099: #endif
        !          4100:                                                              ;
        !          4101:   // Please update GetValidArgList() if you edit longopts
        !          4102:   struct option longopts[] = {
        !          4103:     { "configfile",     required_argument, 0, 'c' },
        !          4104:     { "logfacility",    required_argument, 0, 'l' },
        !          4105:     { "quit",           required_argument, 0, 'q' },
        !          4106:     { "debug",          no_argument,       0, 'd' },
        !          4107:     { "showdirectives", no_argument,       0, 'D' },
        !          4108:     { "interval",       required_argument, 0, 'i' },
        !          4109: #ifndef _WIN32
        !          4110:     { "no-fork",        no_argument,       0, 'n' },
        !          4111: #else
        !          4112:     { "service",        no_argument,       0, 'n' },
        !          4113: #endif
        !          4114:     { "pidfile",        required_argument, 0, 'p' },
        !          4115:     { "report",         required_argument, 0, 'r' },
        !          4116:     { "savestates",     required_argument, 0, 's' },
        !          4117:     { "attributelog",   required_argument, 0, 'A' },
        !          4118:     { "drivedb",        required_argument, 0, 'B' },
        !          4119:     { "version",        no_argument,       0, 'V' },
        !          4120:     { "license",        no_argument,       0, 'V' },
        !          4121:     { "copyright",      no_argument,       0, 'V' },
        !          4122:     { "help",           no_argument,       0, 'h' },
        !          4123:     { "usage",          no_argument,       0, 'h' },
        !          4124: #ifdef HAVE_LIBCAP_NG
        !          4125:     { "capabilities",   no_argument,       0, 'C' },
        !          4126: #endif
        !          4127:     { 0,                0,                 0, 0   }
        !          4128:   };
        !          4129: 
        !          4130:   opterr=optopt=0;
        !          4131:   bool badarg = false;
        !          4132:   bool no_defaultdb = false; // set true on '-B FILE'
        !          4133: 
        !          4134:   // Parse input options.
        !          4135:   int optchar;
        !          4136:   while ((optchar = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
        !          4137:     char *arg;
        !          4138:     char *tailptr;
        !          4139:     long lchecktime;
        !          4140: 
        !          4141:     switch(optchar) {
        !          4142:     case 'q':
        !          4143:       // when to quit
        !          4144:       if (!(strcmp(optarg,"nodev"))) {
        !          4145:         quit=0;
        !          4146:       } else if (!(strcmp(optarg,"nodevstartup"))) {
        !          4147:         quit=1;
        !          4148:       } else if (!(strcmp(optarg,"never"))) {
        !          4149:         quit=2;
        !          4150:       } else if (!(strcmp(optarg,"onecheck"))) {
        !          4151:         quit=3;
        !          4152:         debugmode=1;
        !          4153:       } else if (!(strcmp(optarg,"showtests"))) {
        !          4154:         quit=4;
        !          4155:         debugmode=1;
        !          4156:       } else if (!(strcmp(optarg,"errors"))) {
        !          4157:         quit=5;
        !          4158:       } else {
        !          4159:         badarg = true;
        !          4160:       }
        !          4161:       break;
        !          4162:     case 'l':
        !          4163:       // set the log facility level
        !          4164:       if (!strcmp(optarg, "daemon"))
        !          4165:         facility=LOG_DAEMON;
        !          4166:       else if (!strcmp(optarg, "local0"))
        !          4167:         facility=LOG_LOCAL0;
        !          4168:       else if (!strcmp(optarg, "local1"))
        !          4169:         facility=LOG_LOCAL1;
        !          4170:       else if (!strcmp(optarg, "local2"))
        !          4171:         facility=LOG_LOCAL2;
        !          4172:       else if (!strcmp(optarg, "local3"))
        !          4173:         facility=LOG_LOCAL3;
        !          4174:       else if (!strcmp(optarg, "local4"))
        !          4175:         facility=LOG_LOCAL4;
        !          4176:       else if (!strcmp(optarg, "local5"))
        !          4177:         facility=LOG_LOCAL5;
        !          4178:       else if (!strcmp(optarg, "local6"))
        !          4179:         facility=LOG_LOCAL6;
        !          4180:       else if (!strcmp(optarg, "local7"))
        !          4181:         facility=LOG_LOCAL7;
        !          4182:       else
        !          4183:         badarg = true;
        !          4184:       break;
        !          4185:     case 'd':
        !          4186:       // enable debug mode
        !          4187:       debugmode = 1;
        !          4188:       break;
        !          4189:     case 'n':
        !          4190:       // don't fork()
        !          4191: #ifndef _WIN32 // On Windows, --service is already handled by daemon_main()
        !          4192:       do_fork = false;
        !          4193: #endif
        !          4194:       break;
        !          4195:     case 'D':
        !          4196:       // print summary of all valid directives
        !          4197:       debugmode = 1;
        !          4198:       Directives();
        !          4199:       EXIT(0);
        !          4200:       break;
        !          4201:     case 'i':
        !          4202:       // Period (time interval) for checking
        !          4203:       // strtol will set errno in the event of overflow, so we'll check it.
        !          4204:       errno = 0;
        !          4205:       lchecktime = strtol(optarg, &tailptr, 10);
        !          4206:       if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) {
        !          4207:         debugmode=1;
        !          4208:         PrintHead();
        !          4209:         PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg);
        !          4210:         PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX);
        !          4211:         PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
        !          4212:         EXIT(EXIT_BADCMD);
        !          4213:       }
        !          4214:       checktime = (int)lchecktime;
        !          4215:       break;
        !          4216:     case 'r':
        !          4217:       // report IOCTL transactions
        !          4218:       {
        !          4219:         int i;
        !          4220:         char *s;
        !          4221: 
        !          4222:         // split_report_arg() may modify its first argument string, so use a
        !          4223:         // copy of optarg in case we want optarg for an error message.
        !          4224:         if (!(s = strdup(optarg))) {
        !          4225:           PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n");
        !          4226:           EXIT(EXIT_NOMEM);
        !          4227:         }
        !          4228:         if (split_report_arg(s, &i)) {
        !          4229:           badarg = true;
        !          4230:         } else if (i<1 || i>3) {
        !          4231:           debugmode=1;
        !          4232:           PrintHead();
        !          4233:           PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg);
        !          4234:           PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n");
        !          4235:           EXIT(EXIT_BADCMD);
        !          4236:         } else if (!strcmp(s,"ioctl")) {
        !          4237:           ata_debugmode = scsi_debugmode = i;
        !          4238:         } else if (!strcmp(s,"ataioctl")) {
        !          4239:           ata_debugmode = i;
        !          4240:         } else if (!strcmp(s,"scsiioctl")) {
        !          4241:           scsi_debugmode = i;
        !          4242:         } else {
        !          4243:           badarg = true;
        !          4244:         }
        !          4245:         free(s);  // TODO: use std::string
        !          4246:       }
        !          4247:       break;
        !          4248:     case 'c':
        !          4249:       // alternate configuration file
        !          4250:       if (strcmp(optarg,"-"))
        !          4251:         configfile = (configfile_alt = optarg).c_str();
        !          4252:       else // read from stdin
        !          4253:         configfile=configfile_stdin;
        !          4254:       break;
        !          4255:     case 'p':
        !          4256:       // output file with PID number
        !          4257:       pid_file = optarg;
        !          4258:       break;
        !          4259:     case 's':
        !          4260:       // path prefix of persistent state file
        !          4261:       state_path_prefix = optarg;
        !          4262:       break;
        !          4263:     case 'A':
        !          4264:       // path prefix of attribute log file
        !          4265:       attrlog_path_prefix = optarg;
        !          4266:       break;
        !          4267:     case 'B':
        !          4268:       {
        !          4269:         const char * path = optarg;
        !          4270:         if (*path == '+' && path[1])
        !          4271:           path++;
        !          4272:         else
        !          4273:           no_defaultdb = true;
        !          4274:         unsigned char savedebug = debugmode; debugmode = 1;
        !          4275:         if (!read_drive_database(path))
        !          4276:           EXIT(EXIT_BADCMD);
        !          4277:         debugmode = savedebug;
        !          4278:       }
        !          4279:       break;
        !          4280:     case 'V':
        !          4281:       // print version and CVS info
        !          4282:       debugmode = 1;
        !          4283:       PrintOut(LOG_INFO, "%s", format_version_info("smartd", true /*full*/).c_str());
        !          4284:       EXIT(0);
        !          4285:       break;
        !          4286: #ifdef HAVE_LIBCAP_NG
        !          4287:     case 'C':
        !          4288:       // enable capabilities
        !          4289:       enable_capabilities = true;
        !          4290:       break;
        !          4291: #endif
        !          4292:     case 'h':
        !          4293:       // help: print summary of command-line options
        !          4294:       debugmode=1;
        !          4295:       PrintHead();
        !          4296:       Usage();
        !          4297:       EXIT(0);
        !          4298:       break;
        !          4299:     case '?':
        !          4300:     default:
        !          4301:       // unrecognized option
        !          4302:       debugmode=1;
        !          4303:       PrintHead();
        !          4304:       // Point arg to the argument in which this option was found.
        !          4305:       arg = argv[optind-1];
        !          4306:       // Check whether the option is a long option that doesn't map to -h.
        !          4307:       if (arg[1] == '-' && optchar != 'h') {
        !          4308:         // Iff optopt holds a valid option then argument must be missing.
        !          4309:         if (optopt && (strchr(shortopts, optopt) != NULL)) {
        !          4310:           PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2);
        !          4311:           PrintValidArgs(optopt);
        !          4312:         } else {
        !          4313:           PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
        !          4314:         }
        !          4315:         PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n");
        !          4316:         EXIT(EXIT_BADCMD);
        !          4317:       }
        !          4318:       if (optopt) {
        !          4319:         // Iff optopt holds a valid option then argument must be missing.
        !          4320:         if (strchr(shortopts, optopt) != NULL){
        !          4321:           PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt);
        !          4322:           PrintValidArgs(optopt);
        !          4323:         } else {
        !          4324:           PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
        !          4325:         }
        !          4326:         PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
        !          4327:         EXIT(EXIT_BADCMD);
        !          4328:       }
        !          4329:       Usage();
        !          4330:       EXIT(0);
        !          4331:     }
        !          4332: 
        !          4333:     // Check to see if option had an unrecognized or incorrect argument.
        !          4334:     if (badarg) {
        !          4335:       debugmode=1;
        !          4336:       PrintHead();
        !          4337:       // It would be nice to print the actual option name given by the user
        !          4338:       // here, but we just print the short form.  Please fix this if you know
        !          4339:       // a clean way to do it.
        !          4340:       PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg);
        !          4341:       PrintValidArgs(optchar);
        !          4342:       PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
        !          4343:       EXIT(EXIT_BADCMD);
        !          4344:     }
        !          4345:   }
        !          4346: 
        !          4347:   // non-option arguments are not allowed
        !          4348:   if (argc > optind) {
        !          4349:     debugmode=1;
        !          4350:     PrintHead();
        !          4351:     PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]);
        !          4352:     PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
        !          4353:     EXIT(EXIT_BADCMD);
        !          4354:   }
        !          4355: 
        !          4356:   // no pidfile in debug mode
        !          4357:   if (debugmode && !pid_file.empty()) {
        !          4358:     debugmode=1;
        !          4359:     PrintHead();
        !          4360:     PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n");
        !          4361:     PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file.c_str());
        !          4362:     EXIT(EXIT_BADCMD);
        !          4363:   }
        !          4364: 
        !          4365: #ifndef _WIN32
        !          4366:   if (!debugmode) {
        !          4367:     // absolute path names are required due to chdir('/') after fork().
        !          4368:     check_abs_path('p', pid_file);
        !          4369:     check_abs_path('s', state_path_prefix);
        !          4370:     check_abs_path('A', attrlog_path_prefix);
        !          4371:   }
        !          4372: #endif
        !          4373: 
        !          4374:   // Read or init drive database
        !          4375:   if (!no_defaultdb) {
        !          4376:     unsigned char savedebug = debugmode; debugmode = 1;
        !          4377:     if (!read_default_drive_databases())
        !          4378:         EXIT(EXIT_BADCMD);
        !          4379:     debugmode = savedebug;
        !          4380:   }
        !          4381: 
        !          4382:   // print header
        !          4383:   PrintHead();
        !          4384: }
        !          4385: 
        !          4386: // Function we call if no configuration file was found or if the
        !          4387: // SCANDIRECTIVE Directive was found.  It makes entries for device
        !          4388: // names returned by scan_smart_devices() in os_OSNAME.cpp
        !          4389: static int MakeConfigEntries(const dev_config & base_cfg,
        !          4390:   dev_config_vector & conf_entries, smart_device_list & scanned_devs, const char * type)
        !          4391: {
        !          4392:   // make list of devices
        !          4393:   smart_device_list devlist;
        !          4394:   if (!smi()->scan_smart_devices(devlist, (*type ? type : 0)))
        !          4395:     PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
        !          4396:   
        !          4397:   // if no devices, or error constructing list, return
        !          4398:   if (devlist.size() <= 0)
        !          4399:     return 0;
        !          4400: 
        !          4401:   // add empty device slots for existing config entries
        !          4402:   while (scanned_devs.size() < conf_entries.size())
        !          4403:     scanned_devs.push_back((smart_device *)0);
        !          4404: 
        !          4405:   // loop over entries to create
        !          4406:   for (unsigned i = 0; i < devlist.size(); i++) {
        !          4407:     // Move device pointer
        !          4408:     smart_device * dev = devlist.release(i);
        !          4409:     scanned_devs.push_back(dev);
        !          4410: 
        !          4411:     // Copy configuration, update device and type name
        !          4412:     conf_entries.push_back(base_cfg);
        !          4413:     dev_config & cfg = conf_entries.back();
        !          4414:     cfg.name = dev->get_info().info_name;
        !          4415:     cfg.dev_name = dev->get_info().dev_name;
        !          4416:     cfg.dev_type = type;
        !          4417:   }
        !          4418:   
        !          4419:   return devlist.size();
        !          4420: }
        !          4421:  
        !          4422: static void CanNotRegister(const char *name, const char *type, int line, bool scandirective)
        !          4423: {
        !          4424:   if (!debugmode && scandirective)
        !          4425:     return;
        !          4426:   if (line)
        !          4427:     PrintOut(scandirective?LOG_INFO:LOG_CRIT,
        !          4428:              "Unable to register %s device %s at line %d of file %s\n",
        !          4429:              type, name, line, configfile);
        !          4430:   else
        !          4431:     PrintOut(LOG_INFO,"Unable to register %s device %s\n",
        !          4432:              type, name);
        !          4433:   return;
        !          4434: }
        !          4435: 
        !          4436: // Returns negative value (see ParseConfigFile()) if config file
        !          4437: // had errors, else number of entries which may be zero or positive. 
        !          4438: static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_device_list & scanned_devs)
        !          4439: {
        !          4440:   // parse configuration file configfile (normally /etc/smartd.conf)  
        !          4441:   int entries = ParseConfigFile(conf_entries);
        !          4442: 
        !          4443:   if (entries < 0) {
        !          4444:     // There was an error reading the configuration file.
        !          4445:     conf_entries.clear();
        !          4446:     if (entries == -1)
        !          4447:       PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile);
        !          4448:     return entries;
        !          4449:   }
        !          4450: 
        !          4451:   // no error parsing config file.
        !          4452:   if (entries) {
        !          4453:     // we did not find a SCANDIRECTIVE and did find valid entries
        !          4454:     PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile);
        !          4455:   }
        !          4456:   else if (!conf_entries.empty()) {
        !          4457:     // we found a SCANDIRECTIVE or there was no configuration file so
        !          4458:     // scan.  Configuration file's last entry contains all options
        !          4459:     // that were set
        !          4460:     dev_config first = conf_entries.back();
        !          4461:     conf_entries.pop_back();
        !          4462: 
        !          4463:     if (first.lineno)
        !          4464:       PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE);
        !          4465:     else
        !          4466:       PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile);
        !          4467:     
        !          4468:     // make config list of devices to search for
        !          4469:     MakeConfigEntries(first, conf_entries, scanned_devs, first.dev_type.c_str());
        !          4470: 
        !          4471:     // warn user if scan table found no devices
        !          4472:     if (conf_entries.empty())
        !          4473:       PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n");
        !          4474:   } 
        !          4475:   else
        !          4476:     PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile);
        !          4477:   
        !          4478:   return conf_entries.size();
        !          4479: }
        !          4480: 
        !          4481: 
        !          4482: // This function tries devices from conf_entries.  Each one that can be
        !          4483: // registered is moved onto the [ata|scsi]devices lists and removed
        !          4484: // from the conf_entries list.
        !          4485: static void RegisterDevices(const dev_config_vector & conf_entries, smart_device_list & scanned_devs,
        !          4486:                             dev_config_vector & configs, dev_state_vector & states, smart_device_list & devices)
        !          4487: {
        !          4488:   // start by clearing lists/memory of ALL existing devices
        !          4489:   configs.clear();
        !          4490:   devices.clear();
        !          4491:   states.clear();
        !          4492: 
        !          4493:   // Register entries
        !          4494:   for (unsigned i = 0; i < conf_entries.size(); i++){
        !          4495: 
        !          4496:     dev_config cfg = conf_entries[i];
        !          4497: 
        !          4498:     // get device of appropriate type
        !          4499:     smart_device_auto_ptr dev;
        !          4500:     bool scanning = false;
        !          4501: 
        !          4502:     // Device may already be detected during devicescan
        !          4503:     if (i < scanned_devs.size()) {
        !          4504:       dev = scanned_devs.release(i);
        !          4505:       if (dev)
        !          4506:         scanning = true;
        !          4507:     }
        !          4508: 
        !          4509:     if (!dev) {
        !          4510:       dev = smi()->get_smart_device(cfg.name.c_str(), cfg.dev_type.c_str());
        !          4511:       if (!dev) {
        !          4512:         if (cfg.dev_type.empty())
        !          4513:           PrintOut(LOG_INFO,"Device: %s, unable to autodetect device type\n", cfg.name.c_str());
        !          4514:         else
        !          4515:           PrintOut(LOG_INFO,"Device: %s, unsupported device type '%s'\n", cfg.name.c_str(), cfg.dev_type.c_str());
        !          4516:         continue;
        !          4517:       }
        !          4518:     }
        !          4519: 
        !          4520:     // Save old info
        !          4521:     smart_device::device_info oldinfo = dev->get_info();
        !          4522: 
        !          4523:     // Open with autodetect support, may return 'better' device
        !          4524:     dev.replace( dev->autodetect_open() );
        !          4525: 
        !          4526:     // Report if type has changed
        !          4527:     if (oldinfo.dev_type != dev->get_dev_type())
        !          4528:       PrintOut(LOG_INFO,"Device: %s, type changed from '%s' to '%s'\n",
        !          4529:         cfg.name.c_str(), oldinfo.dev_type.c_str(), dev->get_dev_type());
        !          4530: 
        !          4531:     if (!dev->is_open()) {
        !          4532:       // For linux+devfs, a nonexistent device gives a strange error
        !          4533:       // message.  This makes the error message a bit more sensible.
        !          4534:       // If no debug and scanning - don't print errors
        !          4535:       if (debugmode || !scanning)
        !          4536:         PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", dev->get_info_name(), dev->get_errmsg());
        !          4537:       continue;
        !          4538:     }
        !          4539: 
        !          4540:     // Update informal name
        !          4541:     cfg.name = dev->get_info().info_name;
        !          4542:     PrintOut(LOG_INFO, "Device: %s, opened\n", cfg.name.c_str());
        !          4543: 
        !          4544:     // Prepare initial state
        !          4545:     dev_state state;
        !          4546: 
        !          4547:     // register ATA devices
        !          4548:     if (dev->is_ata()){
        !          4549:       if (ATADeviceScan(cfg, state, dev->to_ata())) {
        !          4550:         CanNotRegister(cfg.name.c_str(), "ATA", cfg.lineno, scanning);
        !          4551:         dev.reset();
        !          4552:       }
        !          4553:     }
        !          4554:     // or register SCSI devices
        !          4555:     else if (dev->is_scsi()){
        !          4556:       if (SCSIDeviceScan(cfg, state, dev->to_scsi())) {
        !          4557:         CanNotRegister(cfg.name.c_str(), "SCSI", cfg.lineno, scanning);
        !          4558:         dev.reset();
        !          4559:       }
        !          4560:     }
        !          4561:     else {
        !          4562:       PrintOut(LOG_INFO, "Device: %s, neither ATA nor SCSI device\n", cfg.name.c_str());
        !          4563:       dev.reset();
        !          4564:     }
        !          4565: 
        !          4566:     if (dev) {
        !          4567:       // move onto the list of devices
        !          4568:       configs.push_back(cfg);
        !          4569:       states.push_back(state);
        !          4570:       devices.push_back(dev);
        !          4571:     }
        !          4572:     // if device is explictly listed and we can't register it, then
        !          4573:     // exit unless the user has specified that the device is removable
        !          4574:     else if (!scanning) {
        !          4575:       if (cfg.removable || quit==2)
        !          4576:         PrintOut(LOG_INFO, "Device %s not available\n", cfg.name.c_str());
        !          4577:       else {
        !          4578:         PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", cfg.name.c_str());
        !          4579:         EXIT(EXIT_BADDEV);
        !          4580:       }
        !          4581:     }
        !          4582:   }
        !          4583: }
        !          4584: 
        !          4585: 
        !          4586: // Main program without exception handling
        !          4587: static int main_worker(int argc, char **argv)
        !          4588: {
        !          4589:   // Initialize interface
        !          4590:   smart_interface::init();
        !          4591:   if (!smi())
        !          4592:     return 1;
        !          4593: 
        !          4594:   // is it our first pass through?
        !          4595:   bool firstpass = true;
        !          4596: 
        !          4597:   // next time to wake up
        !          4598:   time_t wakeuptime = 0;
        !          4599: 
        !          4600:   // parse input and print header and usage info if needed
        !          4601:   ParseOpts(argc,argv);
        !          4602:   
        !          4603:   // Configuration for each device
        !          4604:   dev_config_vector configs;
        !          4605:   // Device states
        !          4606:   dev_state_vector states;
        !          4607:   // Devices to monitor
        !          4608:   smart_device_list devices;
        !          4609: 
        !          4610:   bool write_states_always = true;
        !          4611: 
        !          4612: #ifdef HAVE_LIBCAP_NG
        !          4613:   // Drop capabilities
        !          4614:   if (enable_capabilities) {
        !          4615:     capng_clear(CAPNG_SELECT_BOTH);
        !          4616:     capng_updatev(CAPNG_ADD, (capng_type_t)(CAPNG_EFFECTIVE|CAPNG_PERMITTED),
        !          4617:                   CAP_SYS_ADMIN, CAP_MKNOD, CAP_SYS_RAWIO, -1);
        !          4618:     capng_apply(CAPNG_SELECT_BOTH);
        !          4619:   }
        !          4620: #endif
        !          4621: 
        !          4622:   // the main loop of the code
        !          4623:   for (;;) {
        !          4624: 
        !          4625:     // are we exiting from a signal?
        !          4626:     if (caughtsigEXIT) {
        !          4627:       // are we exiting with SIGTERM?
        !          4628:       int isterm=(caughtsigEXIT==SIGTERM);
        !          4629:       int isquit=(caughtsigEXIT==SIGQUIT);
        !          4630:       int isok=debugmode?isterm || isquit:isterm;
        !          4631:       
        !          4632:       PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n",
        !          4633:                caughtsigEXIT, strsignal(caughtsigEXIT));
        !          4634: 
        !          4635:       if (!isok)
        !          4636:         return EXIT_SIGNAL;
        !          4637: 
        !          4638:       // Write state files
        !          4639:       if (!state_path_prefix.empty())
        !          4640:         write_all_dev_states(configs, states);
        !          4641: 
        !          4642:       return 0;
        !          4643:     }
        !          4644: 
        !          4645:     // Should we (re)read the config file?
        !          4646:     if (firstpass || caughtsigHUP){
        !          4647:       if (!firstpass) {
        !          4648: #ifdef __CYGWIN__
        !          4649:         // Workaround for missing SIGQUIT via keyboard on Cygwin
        !          4650:         if (caughtsigHUP==2) {
        !          4651:           // Simulate SIGQUIT if another SIGINT arrives soon
        !          4652:           caughtsigHUP=0;
        !          4653:           sleep(1);
        !          4654:           if (caughtsigHUP==2) {
        !          4655:             caughtsigEXIT=SIGQUIT;
        !          4656:             continue;
        !          4657:           }
        !          4658:           caughtsigHUP=2;
        !          4659:         }
        !          4660: #endif
        !          4661:         // Write state files
        !          4662:         if (!state_path_prefix.empty())
        !          4663:           write_all_dev_states(configs, states);
        !          4664: 
        !          4665:         PrintOut(LOG_INFO,
        !          4666:                  caughtsigHUP==1?
        !          4667:                  "Signal HUP - rereading configuration file %s\n":
        !          4668:                  "\a\nSignal INT - rereading configuration file %s ("SIGQUIT_KEYNAME" quits)\n\n",
        !          4669:                  configfile);
        !          4670:       }
        !          4671: 
        !          4672:       {
        !          4673:         dev_config_vector conf_entries; // Entries read from smartd.conf
        !          4674:         smart_device_list scanned_devs; // Devices found during scan
        !          4675:         // (re)reads config file, makes >=0 entries
        !          4676:         int entries = ReadOrMakeConfigEntries(conf_entries, scanned_devs);
        !          4677: 
        !          4678:         if (entries>=0) {
        !          4679:           // checks devices, then moves onto ata/scsi list or deallocates.
        !          4680:           RegisterDevices(conf_entries, scanned_devs, configs, states, devices);
        !          4681:           if (!(configs.size() == devices.size() && configs.size() == states.size()))
        !          4682:             throw std::logic_error("Invalid result from RegisterDevices");
        !          4683:         }
        !          4684:         else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) {
        !          4685:           // user has asked to continue on error in configuration file
        !          4686:           if (!firstpass)
        !          4687:             PrintOut(LOG_INFO,"Reusing previous configuration\n");
        !          4688:         }
        !          4689:         else {
        !          4690:           // exit with configuration file error status
        !          4691:           return (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF);
        !          4692:         }
        !          4693:       }
        !          4694: 
        !          4695:       // Log number of devices we are monitoring...
        !          4696:       if (devices.size() > 0 || quit==2 || (quit==1 && !firstpass)) {
        !          4697:         int numata = 0;
        !          4698:         for (unsigned i = 0; i < devices.size(); i++) {
        !          4699:           if (devices.at(i)->is_ata())
        !          4700:             numata++;
        !          4701:         }
        !          4702:         PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
        !          4703:                  numata, devices.size() - numata);
        !          4704:       }
        !          4705:       else {
        !          4706:         PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
        !          4707:         return EXIT_NODEV;
        !          4708:       }
        !          4709: 
        !          4710:       if (quit==4) {
        !          4711:         // user has asked to print test schedule
        !          4712:         PrintTestSchedule(configs, states, devices);
        !          4713:         return 0;
        !          4714:       }
        !          4715: 
        !          4716: #ifdef HAVE_LIBCAP_NG
        !          4717:       if (enable_capabilities) {
        !          4718:         for (unsigned i = 0; i < configs.size(); i++) {
        !          4719:           if (!configs[i].emailaddress.empty() || !configs[i].emailcmdline.empty()) {
        !          4720:             PrintOut(LOG_WARNING, "Mail can't be enabled together with --capabilities. All mail will be suppressed.\n");
        !          4721:             break;
        !          4722:           }
        !          4723:         }
        !          4724:       }
        !          4725: #endif
        !          4726: 
        !          4727:       // reset signal
        !          4728:       caughtsigHUP=0;
        !          4729: 
        !          4730:       // Always write state files after (re)configuration
        !          4731:       write_states_always = true;
        !          4732:     }
        !          4733: 
        !          4734:     // check all devices once,
        !          4735:     // self tests are not started in first pass unless '-q onecheck' is specified
        !          4736:     CheckDevicesOnce(configs, states, devices, firstpass, (!firstpass || quit==3));
        !          4737: 
        !          4738:      // Write state files
        !          4739:     if (!state_path_prefix.empty())
        !          4740:       write_all_dev_states(configs, states, write_states_always);
        !          4741:     write_states_always = false;
        !          4742: 
        !          4743:     // Write attribute logs
        !          4744:     if (!attrlog_path_prefix.empty())
        !          4745:       write_all_dev_attrlogs(configs, states);
        !          4746: 
        !          4747:     // user has asked us to exit after first check
        !          4748:     if (quit==3) {
        !          4749:       PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n"
        !          4750:                "smartd is exiting (exit status 0)\n");
        !          4751:       return 0;
        !          4752:     }
        !          4753:     
        !          4754:     // fork into background if needed
        !          4755:     if (firstpass && !debugmode) {
        !          4756:       DaemonInit();
        !          4757:     }
        !          4758: 
        !          4759:     // set exit and signal handlers, write PID file, set wake-up time
        !          4760:     if (firstpass){
        !          4761:       Initialize(&wakeuptime);
        !          4762:       firstpass = false;
        !          4763:     }
        !          4764:     
        !          4765:     // sleep until next check time, or a signal arrives
        !          4766:     wakeuptime = dosleep(wakeuptime, write_states_always);
        !          4767:   }
        !          4768: }
        !          4769: 
        !          4770: 
        !          4771: #ifndef _WIN32
        !          4772: // Main program
        !          4773: int main(int argc, char **argv)
        !          4774: #else
        !          4775: // Windows: internal main function started direct or by service control manager
        !          4776: static int smartd_main(int argc, char **argv)
        !          4777: #endif
        !          4778: {
        !          4779:   int status;
        !          4780:   try {
        !          4781:     // Do the real work ...
        !          4782:     status = main_worker(argc, argv);
        !          4783:   }
        !          4784:   catch (int ex) {
        !          4785:     // EXIT(status) arrives here
        !          4786:     status = ex;
        !          4787:   }
        !          4788:   catch (const std::bad_alloc & /*ex*/) {
        !          4789:     // Memory allocation failed (also thrown by std::operator new)
        !          4790:     PrintOut(LOG_CRIT, "Smartd: Out of memory\n");
        !          4791:     status = EXIT_NOMEM;
        !          4792:   }
        !          4793:   catch (const std::exception & ex) {
        !          4794:     // Other fatal errors
        !          4795:     PrintOut(LOG_CRIT, "Smartd: Exception: %s\n", ex.what());
        !          4796:     status = EXIT_BADCODE;
        !          4797:   }
        !          4798: 
        !          4799:   if (is_initialized)
        !          4800:     status = Goodbye(status);
        !          4801: 
        !          4802: #ifdef _WIN32
        !          4803:   daemon_winsvc_exitcode = status;
        !          4804: #endif
        !          4805:   return status;
        !          4806: }
        !          4807: 
        !          4808: 
        !          4809: #ifdef _WIN32
        !          4810: // Main function for Windows
        !          4811: int main(int argc, char **argv){
        !          4812:   // Options for smartd windows service
        !          4813:   static const daemon_winsvc_options svc_opts = {
        !          4814:     "--service", // cmd_opt
        !          4815:     "smartd", "SmartD Service", // servicename, displayname
        !          4816:     // description
        !          4817:     "Controls and monitors storage devices using the Self-Monitoring, "
        !          4818:     "Analysis and Reporting Technology System (S.M.A.R.T.) "
        !          4819:     "built into ATA and SCSI Hard Drives. "
        !          4820:     PACKAGE_HOMEPAGE
        !          4821:   };
        !          4822:   // daemon_main() handles daemon and service specific commands
        !          4823:   // and starts smartd_main() direct, from a new process,
        !          4824:   // or via service control manager
        !          4825:   return daemon_main("smartd", &svc_opts , smartd_main, argc, argv);
        !          4826: }
        !          4827: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>