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