version 1.1.1.1, 2012/02/21 16:32:16
|
version 1.1.1.3, 2013/07/22 01:17:36
|
Line 4
|
Line 4
|
* Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net> |
* Copyright (C) 2002-11 Bruce Allen <smartmontools-support@lists.sourceforge.net> |
* Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> |
* Copyright (C) 2000 Michael Cornwell <cornwell@acm.org> |
* Copyright (C) 2008 Oliver Bock <brevilo@users.sourceforge.net> |
* Copyright (C) 2008 Oliver Bock <brevilo@users.sourceforge.net> |
* Copyright (C) 2008-11 Christian Franke <smartmontools-support@lists.sourceforge.net> | * Copyright (C) 2008-13 Christian Franke <smartmontools-support@lists.sourceforge.net> |
* |
* |
* This program is free software; you can redistribute it and/or modify |
* This program is free software; you can redistribute it and/or modify |
* it under the terms of the GNU General Public License as published by |
* it under the terms of the GNU General Public License as published by |
Line 21
|
Line 21
|
* |
* |
*/ |
*/ |
|
|
#ifndef _GNU_SOURCE |
|
// TODO: Why is this define necessary? |
|
#define _GNU_SOURCE |
|
#endif |
|
|
|
// unconditionally included files |
// unconditionally included files |
#include <stdio.h> |
#include <stdio.h> |
#include <sys/types.h> |
#include <sys/types.h> |
Line 95 typedef int pid_t;
|
Line 90 typedef int pid_t;
|
#endif |
#endif |
|
|
#ifdef _WIN32 |
#ifdef _WIN32 |
#include "hostname_win32.h" // gethost/domainname() |
|
#define HAVE_GETHOSTNAME 1 |
|
#define HAVE_GETDOMAINNAME 1 |
|
// fork()/signal()/initd simulation for native Windows |
// fork()/signal()/initd simulation for native Windows |
#include "daemon_win32.h" // daemon_main/detach/signal() |
#include "daemon_win32.h" // daemon_main/detach/signal() |
#undef SIGNALFN |
#undef SIGNALFN |
Line 108 typedef int pid_t;
|
Line 100 typedef int pid_t;
|
#define SIGQUIT SIGBREAK |
#define SIGQUIT SIGBREAK |
#define SIGQUIT_KEYNAME "CONTROL-Break" |
#define SIGQUIT_KEYNAME "CONTROL-Break" |
#else // _WIN32 |
#else // _WIN32 |
#ifdef __CYGWIN__ |
|
// 2x CONTROL-C simulates missing SIGQUIT via keyboard |
|
#define SIGQUIT_KEYNAME "2x CONTROL-C" |
|
#else // __CYGWIN__ |
|
#define SIGQUIT_KEYNAME "CONTROL-\\" |
#define SIGQUIT_KEYNAME "CONTROL-\\" |
#endif // __CYGWIN__ |
|
#endif // _WIN32 |
#endif // _WIN32 |
|
|
#if defined (__SVR4) && defined (__sun) |
#if defined (__SVR4) && defined (__sun) |
extern "C" int getdomainname(char *, int); // no declaration in header files! |
extern "C" int getdomainname(char *, int); // no declaration in header files! |
#endif |
#endif |
|
|
#define ARGUSED(x) ((void)(x)) |
|
|
|
const char * smartd_cpp_cvsid = "$Id$" |
const char * smartd_cpp_cvsid = "$Id$" |
CONFIG_H_CVSID; |
CONFIG_H_CVSID; |
|
|
Line 173 static const char * const configfile_stdin = "<stdin>"
|
Line 158 static const char * const configfile_stdin = "<stdin>"
|
// path of alternate configuration file |
// path of alternate configuration file |
static std::string configfile_alt; |
static std::string configfile_alt; |
|
|
|
// warning script file |
|
static std::string warning_script; |
|
|
// command-line: when should we exit? |
// command-line: when should we exit? |
static int quit=0; |
static int quit=0; |
|
|
Line 189 static bool do_fork=true;
|
Line 177 static bool do_fork=true;
|
static bool enable_capabilities = false; |
static bool enable_capabilities = false; |
#endif |
#endif |
|
|
#if defined(_WIN32) || defined(__CYGWIN__) |
|
// TODO: This smartctl only variable is also used in os_win32.cpp |
// TODO: This smartctl only variable is also used in os_win32.cpp |
unsigned char failuretest_permissive = 0; |
unsigned char failuretest_permissive = 0; |
#endif |
|
|
|
// set to one if we catch a USR1 (check devices now) |
// set to one if we catch a USR1 (check devices now) |
static volatile int caughtsigUSR1=0; |
static volatile int caughtsigUSR1=0; |
Line 211 static volatile int caughtsigEXIT=0;
|
Line 197 static volatile int caughtsigEXIT=0;
|
|
|
// This function prints either to stdout or to the syslog as needed. |
// This function prints either to stdout or to the syslog as needed. |
static void PrintOut(int priority, const char *fmt, ...) |
static void PrintOut(int priority, const char *fmt, ...) |
__attribute__ ((format(printf, 2, 3))); | __attribute_format_printf(2, 3); |
|
|
// Attribute monitoring flags. |
// Attribute monitoring flags. |
// See monitor_attr_flags below. |
// See monitor_attr_flags below. |
Line 253 struct dev_config
|
Line 239 struct dev_config
|
std::string name; // Device name (with optional extra info) |
std::string name; // Device name (with optional extra info) |
std::string dev_name; // Device name (plain, for SMARTD_DEVICE variable) |
std::string dev_name; // Device name (plain, for SMARTD_DEVICE variable) |
std::string dev_type; // Device type argument from -d directive, empty if none |
std::string dev_type; // Device type argument from -d directive, empty if none |
|
std::string dev_idinfo; // Device identify info for warning emails |
std::string state_file; // Path of the persistent state file, empty if none |
std::string state_file; // Path of the persistent state file, empty if none |
std::string attrlog_file; // Path of the persistent attrlog file, empty if none |
std::string attrlog_file; // Path of the persistent attrlog file, empty if none |
|
bool ignore; // Ignore this entry |
bool smartcheck; // Check SMART status |
bool smartcheck; // Check SMART status |
bool usagefailed; // Check for failed Usage Attributes |
bool usagefailed; // Check for failed Usage Attributes |
bool prefail; // Track changes in Prefail Attributes |
bool prefail; // Track changes in Prefail Attributes |
Line 263 struct dev_config
|
Line 251 struct dev_config
|
bool errorlog; // Monitor number of ATA errors |
bool errorlog; // Monitor number of ATA errors |
bool xerrorlog; // Monitor number of ATA errors (Extended Comprehensive error log) |
bool xerrorlog; // Monitor number of ATA errors (Extended Comprehensive error log) |
bool offlinests; // Monitor changes in offline data collection status |
bool offlinests; // Monitor changes in offline data collection status |
|
bool offlinests_ns; // Disable auto standby if in progress |
bool selfteststs; // Monitor changes in self-test execution status |
bool selfteststs; // Monitor changes in self-test execution status |
|
bool selfteststs_ns; // Disable auto standby if in progress |
bool permissive; // Ignore failed SMART commands |
bool permissive; // Ignore failed SMART commands |
char autosave; // 1=disable, 2=enable Autosave Attributes |
char autosave; // 1=disable, 2=enable Autosave Attributes |
char autoofflinetest; // 1=disable, 2=enable Auto Offline Test |
char autoofflinetest; // 1=disable, 2=enable Auto Offline Test |
unsigned char fix_firmwarebug; // FIX_*, see atacmds.h | firmwarebug_defs firmwarebugs; // -F directives from drivedb or smartd.conf |
bool ignorepresets; // Ignore database of -v options |
bool ignorepresets; // Ignore database of -v options |
bool showpresets; // Show database entry for this device |
bool showpresets; // Show database entry for this device |
bool removable; // Device may disappear (not be present) |
bool removable; // Device may disappear (not be present) |
Line 285 struct dev_config
|
Line 275 struct dev_config
|
bool emailtest; // Send test email? |
bool emailtest; // Send test email? |
|
|
// ATA ONLY |
// ATA ONLY |
|
int dev_rpm; // rotation rate, 0 = unknown, 1 = SSD, >1 = HDD |
|
int set_aam; // disable(-1), enable(1..255->0..254) Automatic Acoustic Management |
|
int set_apm; // disable(-1), enable(2..255->1..254) Advanced Power Management |
|
int set_lookahead; // disable(-1), enable(1) read look-ahead |
|
int set_standby; // set(1..255->0..254) standby timer |
|
bool set_security_freeze; // Freeze ATA security |
|
int set_wcache; // disable(-1), enable(1) write cache |
|
|
bool sct_erc_set; // set SCT ERC to: |
bool sct_erc_set; // set SCT ERC to: |
unsigned short sct_erc_readtime; // ERC read time (deciseconds) |
unsigned short sct_erc_readtime; // ERC read time (deciseconds) |
unsigned short sct_erc_writetime; // ERC write time (deciseconds) |
unsigned short sct_erc_writetime; // ERC write time (deciseconds) |
Line 303 struct dev_config
|
Line 301 struct dev_config
|
|
|
dev_config::dev_config() |
dev_config::dev_config() |
: lineno(0), |
: lineno(0), |
|
ignore(false), |
smartcheck(false), |
smartcheck(false), |
usagefailed(false), |
usagefailed(false), |
prefail(false), |
prefail(false), |
Line 310 dev_config::dev_config()
|
Line 309 dev_config::dev_config()
|
selftest(false), |
selftest(false), |
errorlog(false), |
errorlog(false), |
xerrorlog(false), |
xerrorlog(false), |
offlinests(false), | offlinests(false), offlinests_ns(false), |
selfteststs(false), | selfteststs(false), selfteststs_ns(false), |
permissive(false), |
permissive(false), |
autosave(0), |
autosave(0), |
autoofflinetest(0), |
autoofflinetest(0), |
fix_firmwarebug(FIX_NOTSPECIFIED), |
|
ignorepresets(false), |
ignorepresets(false), |
showpresets(false), |
showpresets(false), |
removable(false), |
removable(false), |
Line 326 dev_config::dev_config()
|
Line 324 dev_config::dev_config()
|
tempinfo(0), tempcrit(0), |
tempinfo(0), tempcrit(0), |
emailfreq(0), |
emailfreq(0), |
emailtest(false), |
emailtest(false), |
|
dev_rpm(0), |
|
set_aam(0), set_apm(0), |
|
set_lookahead(0), |
|
set_standby(0), |
|
set_security_freeze(false), |
|
set_wcache(0), |
sct_erc_set(false), |
sct_erc_set(false), |
sct_erc_readtime(0), sct_erc_writetime(0), |
sct_erc_readtime(0), sct_erc_writetime(0), |
curr_pending_id(0), offl_pending_id(0), |
curr_pending_id(0), offl_pending_id(0), |
Line 379 struct persistent_dev_state
|
Line 383 struct persistent_dev_state
|
ata_attribute() : id(0), val(0), worst(0), raw(0), resvd(0) { } |
ata_attribute() : id(0), val(0), worst(0), raw(0), resvd(0) { } |
}; |
}; |
ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES]; |
ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES]; |
|
|
|
// SCSI ONLY |
|
|
|
struct scsi_error_counter { |
|
struct scsiErrorCounter errCounter; |
|
unsigned char found; |
|
scsi_error_counter() : found(0) { } |
|
}; |
|
scsi_error_counter scsi_error_counters[3]; |
|
|
|
struct scsi_nonmedium_error { |
|
struct scsiNonMediumError nme; |
|
unsigned char found; |
|
scsi_nonmedium_error() : found(0) { } |
|
}; |
|
scsi_nonmedium_error scsi_nonmedium_error; |
|
|
persistent_dev_state(); |
persistent_dev_state(); |
}; |
}; |
|
|
Line 414 struct temp_dev_state
|
Line 434 struct temp_dev_state
|
// SCSI ONLY |
// SCSI ONLY |
unsigned char SmartPageSupported; // has log sense IE page (0x2f) |
unsigned char SmartPageSupported; // has log sense IE page (0x2f) |
unsigned char TempPageSupported; // has log sense temperature page (0xd) |
unsigned char TempPageSupported; // has log sense temperature page (0xd) |
|
unsigned char ReadECounterPageSupported; |
|
unsigned char WriteECounterPageSupported; |
|
unsigned char VerifyECounterPageSupported; |
|
unsigned char NonMediumErrorPageSupported; |
unsigned char SuppressReport; // minimize nuisance reports |
unsigned char SuppressReport; // minimize nuisance reports |
unsigned char modese_len; // mode sense/select cmd len: 0 (don't |
unsigned char modese_len; // mode sense/select cmd len: 0 (don't |
// know yet) 6 or 10 |
// know yet) 6 or 10 |
|
|
// ATA ONLY |
// ATA ONLY |
uint64_t num_sectors; // Number of sectors |
uint64_t num_sectors; // Number of sectors |
ata_smart_values smartval; // SMART data |
ata_smart_values smartval; // SMART data |
ata_smart_thresholds_pvt smartthres; // SMART thresholds |
ata_smart_thresholds_pvt smartthres; // SMART thresholds |
|
bool offline_started; // true if offline data collection was started |
|
bool selftest_started; // true if self-test was started |
|
|
temp_dev_state(); |
temp_dev_state(); |
}; |
}; |
Line 439 temp_dev_state::temp_dev_state()
|
Line 464 temp_dev_state::temp_dev_state()
|
powerskipcnt(0), |
powerskipcnt(0), |
SmartPageSupported(false), |
SmartPageSupported(false), |
TempPageSupported(false), |
TempPageSupported(false), |
|
ReadECounterPageSupported(false), |
|
WriteECounterPageSupported(false), |
|
VerifyECounterPageSupported(false), |
|
NonMediumErrorPageSupported(false), |
SuppressReport(false), |
SuppressReport(false), |
modese_len(0), |
modese_len(0), |
num_sectors(0) | num_sectors(0), |
| offline_started(false), |
| selftest_started(false) |
{ |
{ |
memset(&smartval, 0, sizeof(smartval)); |
memset(&smartval, 0, sizeof(smartval)); |
memset(&smartthres, 0, sizeof(smartthres)); |
memset(&smartthres, 0, sizeof(smartthres)); |
Line 708 static bool write_dev_state(const char * path, const p
|
Line 739 static bool write_dev_state(const char * path, const p
|
} |
} |
|
|
// Write to the attrlog file |
// Write to the attrlog file |
static bool write_dev_attrlog(const char * path, const persistent_dev_state & state) | static bool write_dev_attrlog(const char * path, const dev_state & state) |
{ |
{ |
stdio_file f(path, "a"); |
stdio_file f(path, "a"); |
if (!f) { |
if (!f) { |
Line 716 static bool write_dev_attrlog(const char * path, const
|
Line 747 static bool write_dev_attrlog(const char * path, const
|
return false; |
return false; |
} |
} |
|
|
// ATA ONLY | |
time_t now = time(0); |
time_t now = time(0); |
struct tm * tms = gmtime(&now); |
struct tm * tms = gmtime(&now); |
fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;", |
fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;", |
1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday, |
1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday, |
tms->tm_hour, tms->tm_min, tms->tm_sec); |
tms->tm_hour, tms->tm_min, tms->tm_sec); |
|
// ATA ONLY |
for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { |
for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) { |
const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i]; |
const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i]; |
if (!pa.id) |
if (!pa.id) |
continue; |
continue; |
fprintf(f, "\t%d;%d;%"PRIu64";", pa.id, pa.val, pa.raw); |
fprintf(f, "\t%d;%d;%"PRIu64";", pa.id, pa.val, pa.raw); |
} |
} |
|
// SCSI ONLY |
|
const struct scsiErrorCounter * ecp; |
|
const char * pageNames[3] = {"read", "write", "verify"}; |
|
for (int k = 0; k < 3; ++k) { |
|
if ( !state.scsi_error_counters[k].found ) continue; |
|
ecp = &state.scsi_error_counters[k].errCounter; |
|
fprintf(f, "\t%s-corr-by-ecc-fast;%"PRIu64";" |
|
"\t%s-corr-by-ecc-delayed;%"PRIu64";" |
|
"\t%s-corr-by-retry;%"PRIu64";" |
|
"\t%s-total-err-corrected;%"PRIu64";" |
|
"\t%s-corr-algorithm-invocations;%"PRIu64";" |
|
"\t%s-gb-processed;%.3f;" |
|
"\t%s-total-unc-errors;%"PRIu64";", |
|
pageNames[k], ecp->counter[0], |
|
pageNames[k], ecp->counter[1], |
|
pageNames[k], ecp->counter[2], |
|
pageNames[k], ecp->counter[3], |
|
pageNames[k], ecp->counter[4], |
|
pageNames[k], (ecp->counter[5] / 1000000000.0), |
|
pageNames[k], ecp->counter[6]); |
|
} |
|
if(state.scsi_nonmedium_error.found && state.scsi_nonmedium_error.nme.gotPC0) { |
|
fprintf(f, "\tnon-medium-errors;%"PRIu64";", state.scsi_nonmedium_error.nme.counterPC0); |
|
} |
|
// write SCSI current temperature if it is monitored |
|
if(state.TempPageSupported && state.temperature) |
|
fprintf(f, "\ttemperature;%d;", state.temperature); |
|
// end of line |
fprintf(f, "\n"); |
fprintf(f, "\n"); |
|
|
return true; |
return true; |
} |
} |
|
|
Line 836 static int Goodbye(int status)
|
Line 895 static int Goodbye(int status)
|
return status; |
return status; |
} |
} |
|
|
#define ENVLENGTH 1024 |
|
|
|
// a replacement for setenv() which is not available on all platforms. |
// a replacement for setenv() which is not available on all platforms. |
// Note that the string passed to putenv must not be freed or made |
// Note that the string passed to putenv must not be freed or made |
// invalid, since a pointer to it is kept by putenv(). This means that |
// invalid, since a pointer to it is kept by putenv(). This means that |
// it must either be a static buffer or allocated off the heap. The |
// it must either be a static buffer or allocated off the heap. The |
// string can be freed if the environment variable is redefined or | // string can be freed if the environment variable is redefined via |
// deleted via another call to putenv(). So we keep these on the stack | // another call to putenv(). There is no portable way to unset a variable |
// as long as the popen() call is underway. | // with putenv(). So we manage the buffer in a static object. |
static int exportenv(char *stackspace, const char *name, const char *value) | // Using setenv() if available is not considered because some |
| // implementations may produce memory leaks. |
| |
| class env_buffer |
{ |
{ |
snprintf(stackspace,ENVLENGTH, "%s=%s", name, value); | public: |
return putenv(stackspace); | env_buffer() |
} | : m_buf((char *)0) { } |
|
|
static char *dnsdomain(const char *hostname) | void set(const char * name, const char * value); |
| |
| private: |
| char * m_buf; |
| |
| env_buffer(const env_buffer &); |
| void operator=(const env_buffer &); |
| }; |
| |
| void env_buffer::set(const char * name, const char * value) |
{ |
{ |
char *p = NULL; | int size = strlen(name) + 1 + strlen(value) + 1; |
#ifdef HAVE_GETADDRINFO | char * newbuf = new char[size]; |
static char canon_name[NI_MAXHOST]; | snprintf(newbuf, size, "%s=%s", name, value); |
struct addrinfo *info = NULL; | |
struct addrinfo hints; | |
int err; | |
|
|
memset(&hints, 0, sizeof(hints)); | if (putenv(newbuf)) |
hints.ai_flags = AI_CANONNAME; | throw std::runtime_error("putenv() failed"); |
if ((err = getaddrinfo(hostname, NULL, &hints, &info)) || (!info)) { | |
PrintOut(LOG_CRIT, "Error retrieving getaddrinfo(%s): %s\n", hostname, gai_strerror(err)); | // This assumes that the same NAME is passed on each call |
return NULL; | delete [] m_buf; |
} | m_buf = newbuf; |
if (info->ai_canonname) { | |
strncpy(canon_name, info->ai_canonname, sizeof(canon_name)); | |
canon_name[NI_MAXHOST - 1] = '\0'; | |
p = canon_name; | |
if ((p = strchr(canon_name, '.'))) | |
p++; | |
} | |
freeaddrinfo(info); | |
#elif HAVE_GETHOSTBYNAME | |
struct hostent *hp; | |
if ((hp = gethostbyname(hostname))) { | |
// Does this work if gethostbyname() returns an IPv6 name in | |
// colon/dot notation? [BA] | |
if ((p = strchr(hp->h_name, '.'))) | |
p++; // skip "." | |
} | |
#else | |
ARGUSED(hostname); | |
#endif | |
return p; | |
} |
} |
|
|
#define EBUFLEN 1024 |
#define EBUFLEN 1024 |
|
|
static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) |
static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) |
__attribute__ ((format (printf, 4, 5))); | __attribute_format_printf(4, 5); |
|
|
// If either address or executable path is non-null then send and log |
// If either address or executable path is non-null then send and log |
// a warning email, or execute executable |
// a warning email, or execute executable |
static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...){ | static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) |
char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024]; | { |
char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN]; | static const char * const whichfail[] = { |
char environ_strings[11][ENVLENGTH]; | |
time_t epoch; | |
va_list ap; | |
const int day=24*3600; | |
int days=0; | |
const char * const whichfail[]={ | |
"EmailTest", // 0 |
"EmailTest", // 0 |
"Health", // 1 |
"Health", // 1 |
"Usage", // 2 |
"Usage", // 2 |
Line 919 static void MailWarning(const dev_config & cfg, dev_st
|
Line 959 static void MailWarning(const dev_config & cfg, dev_st
|
"Temperature" // 12 |
"Temperature" // 12 |
}; |
}; |
|
|
const char *unknown="[Unknown]"; |
|
|
|
// See if user wants us to send mail |
// See if user wants us to send mail |
if (cfg.emailaddress.empty() && cfg.emailcmdline.empty()) |
if (cfg.emailaddress.empty() && cfg.emailcmdline.empty()) |
return; |
return; |
Line 951 static void MailWarning(const dev_config & cfg, dev_st
|
Line 989 static void MailWarning(const dev_config & cfg, dev_st
|
return; |
return; |
|
|
// To decide if to send mail, we need to know what time it is. |
// To decide if to send mail, we need to know what time it is. |
epoch=time(NULL); | time_t epoch = time(0); |
|
|
// Return if less than one day has gone by |
// Return if less than one day has gone by |
|
const int day = 24*3600; |
if (cfg.emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) |
if (cfg.emailfreq==2 && mail->logged && epoch<(mail->lastsent+day)) |
return; |
return; |
|
|
// Return if less than 2^(logged-1) days have gone by |
// Return if less than 2^(logged-1) days have gone by |
if (cfg.emailfreq==3 && mail->logged) { |
if (cfg.emailfreq==3 && mail->logged) { |
days=0x01<<(mail->logged-1); | int days = 0x01 << (mail->logged - 1); |
days*=day; |
days*=day; |
if (epoch<(mail->lastsent+days)) |
if (epoch<(mail->lastsent+days)) |
return; |
return; |
Line 977 static void MailWarning(const dev_config & cfg, dev_st
|
Line 1016 static void MailWarning(const dev_config & cfg, dev_st
|
if (!mail->logged) |
if (!mail->logged) |
mail->firstsent=epoch; |
mail->firstsent=epoch; |
mail->lastsent=epoch; |
mail->lastsent=epoch; |
| |
// get system host & domain names (not null terminated if length=MAX) | |
#ifdef HAVE_GETHOSTNAME | |
if (gethostname(hostname, 256)) | |
strcpy(hostname, unknown); | |
else { | |
char *p=NULL; | |
hostname[255]='\0'; | |
p = dnsdomain(hostname); | |
if (p && *p) { | |
strncpy(domainname, p, 255); | |
domainname[255]='\0'; | |
} else | |
strcpy(domainname, unknown); | |
} | |
#else | |
strcpy(hostname, unknown); | |
strcpy(domainname, unknown); | |
#endif | |
| |
#ifdef HAVE_GETDOMAINNAME | |
if (getdomainname(nisdomain, 256)) | |
strcpy(nisdomain, unknown); | |
else | |
nisdomain[255]='\0'; | |
#else | |
strcpy(nisdomain, unknown); | |
#endif | |
| |
// print warning string into message |
// print warning string into message |
|
char message[256]; |
|
va_list ap; |
va_start(ap, fmt); |
va_start(ap, fmt); |
vsnprintf(message, 256, fmt, ap); | vsnprintf(message, sizeof(message), fmt, ap); |
va_end(ap); |
va_end(ap); |
|
|
// appropriate message about further information |
|
additional[0]=original[0]=further[0]='\0'; |
|
if (which) { |
|
sprintf(further,"You can also use the smartctl utility for further investigation.\n"); |
|
|
|
switch (cfg.emailfreq) { |
|
case 1: |
|
sprintf(additional,"No additional email messages about this problem will be sent.\n"); |
|
break; |
|
case 2: |
|
sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n"); |
|
break; |
|
case 3: |
|
sprintf(additional,"Another email message will be sent in %d days if the problem persists\n", |
|
(0x01)<<mail->logged); |
|
break; |
|
} |
|
if (cfg.emailfreq>1 && mail->logged) { |
|
dateandtimezoneepoch(dates, mail->firstsent); |
|
sprintf(original,"The original email about this issue was sent at %s\n", dates); |
|
} |
|
} |
|
|
|
snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname); |
|
|
|
// If the user has set cfg.emailcmdline, use that as mailer, else "mail" or "mailx". |
|
if (!*executable) |
|
#ifdef DEFAULT_MAILER |
|
executable = DEFAULT_MAILER ; |
|
#else |
|
#ifndef _WIN32 |
|
executable = "mail"; |
|
#else |
|
executable = "blat"; // http://blat.sourceforge.net/ |
|
#endif |
|
#endif |
|
|
|
#ifndef _WIN32 // blat mailer needs comma |
|
// replace commas by spaces to separate recipients |
// replace commas by spaces to separate recipients |
std::replace(address.begin(), address.end(), ',', ' '); |
std::replace(address.begin(), address.end(), ',', ' '); |
#endif | |
// Export information in environment variables that will be useful |
// Export information in environment variables that will be useful |
// for user scripts |
// for user scripts |
exportenv(environ_strings[0], "SMARTD_MAILER", executable); | static env_buffer env[12]; |
exportenv(environ_strings[1], "SMARTD_MESSAGE", message); | env[0].set("SMARTD_MAILER", executable); |
exportenv(environ_strings[2], "SMARTD_SUBJECT", subject); | env[1].set("SMARTD_MESSAGE", message); |
| char dates[DATEANDEPOCHLEN]; |
| snprintf(dates, sizeof(dates), "%d", mail->logged); |
| env[2].set("SMARTD_PREVCNT", dates); |
dateandtimezoneepoch(dates, mail->firstsent); |
dateandtimezoneepoch(dates, mail->firstsent); |
exportenv(environ_strings[3], "SMARTD_TFIRST", dates); | env[3].set("SMARTD_TFIRST", dates); |
snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent); |
snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent); |
exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates); | env[4].set("SMARTD_TFIRSTEPOCH", dates); |
exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]); | env[5].set("SMARTD_FAILTYPE", whichfail[which]); |
if (!address.empty()) | env[6].set("SMARTD_ADDRESS", address.c_str()); |
exportenv(environ_strings[6], "SMARTD_ADDRESS", address.c_str()); | env[7].set("SMARTD_DEVICESTRING", cfg.name.c_str()); |
exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg.name.c_str()); | |
|
|
// Allow 'smartctl ... -d $SMARTD_DEVICETYPE $SMARTD_DEVICE' |
// Allow 'smartctl ... -d $SMARTD_DEVICETYPE $SMARTD_DEVICE' |
exportenv(environ_strings[8], "SMARTD_DEVICETYPE", | env[8].set("SMARTD_DEVICETYPE", |
(!cfg.dev_type.empty() ? cfg.dev_type.c_str() : "auto")); | (!cfg.dev_type.empty() ? cfg.dev_type.c_str() : "auto")); |
exportenv(environ_strings[9], "SMARTD_DEVICE", cfg.dev_name.c_str()); | env[9].set("SMARTD_DEVICE", cfg.dev_name.c_str()); |
|
|
snprintf(fullmessage, 1024, | env[10].set("SMARTD_DEVICEINFO", cfg.dev_idinfo.c_str()); |
"This email was generated by the smartd daemon running on:\n\n" | dates[0] = 0; |
" host name: %s\n" | if (which) switch (cfg.emailfreq) { |
" DNS domain: %s\n" | case 2: dates[0] = '1'; dates[1] = 0; break; |
" NIS domain: %s\n\n" | case 3: snprintf(dates, sizeof(dates), "%d", (0x01)<<mail->logged); |
"The following warning/error was logged by the smartd daemon:\n\n" | } |
"%s\n\n" | env[11].set("SMARTD_NEXTDAYS", dates); |
"For details see host's SYSLOG.\n\n" | |
"%s%s%s", | |
hostname, domainname, nisdomain, message, further, original, additional); | |
exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage); | |
|
|
// now construct a command to send this as EMAIL |
// now construct a command to send this as EMAIL |
#ifndef _WIN32 | char command[2048]; |
if (!address.empty()) | if (!*executable) |
snprintf(command, 2048, | executable = "<mail>"; |
"$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n" | |
"%sENDMAIL\n", subject, address.c_str(), fullmessage); | |
else | |
snprintf(command, 2048, "%s 2>&1", executable); | |
| |
// tell SYSLOG what we are about to do... | |
const char * newadd = (!address.empty()? address.c_str() : "<nomailer>"); |
const char * newadd = (!address.empty()? address.c_str() : "<nomailer>"); |
const char * newwarn = (which? "Warning via" : "Test of"); |
const char * newwarn = (which? "Warning via" : "Test of"); |
|
|
|
#ifndef _WIN32 |
|
snprintf(command, sizeof(command), "%s 2>&1", warning_script.c_str()); |
|
|
|
// tell SYSLOG what we are about to do... |
PrintOut(LOG_INFO,"%s %s to %s ...\n", |
PrintOut(LOG_INFO,"%s %s to %s ...\n", |
which?"Sending warning via":"Executing test of", executable, newadd); |
which?"Sending warning via":"Executing test of", executable, newadd); |
|
|
Line 1169 static void MailWarning(const dev_config & cfg, dev_st
|
Line 1140 static void MailWarning(const dev_config & cfg, dev_st
|
} |
} |
|
|
#else // _WIN32 |
#else // _WIN32 |
|
{ |
|
snprintf(command, sizeof(command), "cmd /c \"%s\"", warning_script.c_str()); |
|
|
// No "here-documents" on Windows, so must use separate commandline and stdin |
|
char stdinbuf[1024]; |
|
command[0] = stdinbuf[0] = 0; |
|
int boxtype = -1, boxmsgoffs = 0; |
|
const char * newadd = "<nomailer>"; |
|
if (!address.empty()) { |
|
// address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox |
|
char addr1[9+1+13] = ""; int n1 = -1, n2 = -1; |
|
if (sscanf(address.c_str(), "%9[a-z]%n,%n", addr1, &n1, &n2) == 1 && (n1 == (int)address.size() || n2 > 0)) { |
|
if (!strcmp(addr1, "msgbox")) |
|
boxtype = 0; |
|
else if (!strcmp(addr1, "sysmsgbox")) |
|
boxtype = 1; |
|
if (boxtype >= 0) |
|
address.erase(0, (n2 > n1 ? n2 : n1)); |
|
} |
|
|
|
if (!address.empty()) { |
|
// Use "blat" parameter syntax (TODO: configure via -M for other mailers) |
|
snprintf(command, sizeof(command), |
|
"%s - -q -subject \"%s\" -to \"%s\"", |
|
executable, subject, address.c_str()); |
|
newadd = address.c_str(); |
|
} |
|
|
|
// Message for mail [0...] and messagebox [boxmsgoffs...] |
|
snprintf(stdinbuf, sizeof(stdinbuf), |
|
"This email was generated by the smartd daemon running on:\n\n" |
|
" host name: %s\n" |
|
" DNS domain: %s\n" |
|
// " NIS domain: %s\n" |
|
"\n", |
|
hostname, /*domainname, */ nisdomain); |
|
boxmsgoffs = strlen(stdinbuf); |
|
snprintf(stdinbuf+boxmsgoffs, sizeof(stdinbuf)-boxmsgoffs, |
|
"The following warning/error was logged by the smartd daemon:\n\n" |
|
"%s\n\n" |
|
"For details see the event log or log file of smartd.\n\n" |
|
"%s%s%s" |
|
"\n", |
|
message, further, original, additional); |
|
} |
|
else |
|
snprintf(command, sizeof(command), "%s", executable); |
|
|
|
const char * newwarn = (which ? "Warning via" : "Test of"); |
|
if (boxtype >= 0) { |
|
// show message box |
|
daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs); |
|
PrintOut(LOG_INFO,"%s message box\n", newwarn); |
|
} |
|
if (command[0]) { |
|
char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog() |
char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog() |
int rc; |
int rc; |
// run command |
// run command |
PrintOut(LOG_INFO,"%s %s to %s ...\n", |
PrintOut(LOG_INFO,"%s %s to %s ...\n", |
(which?"Sending warning via":"Executing test of"), executable, newadd); |
(which?"Sending warning via":"Executing test of"), executable, newadd); |
rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf)); | rc = daemon_spawn(command, "", 0, stdoutbuf, sizeof(stdoutbuf)); |
if (rc >= 0 && stdoutbuf[0]) |
if (rc >= 0 && stdoutbuf[0]) |
PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n", |
PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n", |
newwarn, executable, newadd, (int)strlen(stdoutbuf), stdoutbuf); |
newwarn, executable, newadd, (int)strlen(stdoutbuf), stdoutbuf); |
Line 1245 static void MailWarning(const dev_config & cfg, dev_st
|
Line 1166 static void MailWarning(const dev_config & cfg, dev_st
|
} |
} |
|
|
static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) |
static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) |
__attribute__ ((format (printf, 4, 5))); | __attribute_format_printf(4, 5); |
|
|
static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) |
static void reset_warning_mail(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...) |
{ |
{ |
Line 1308 void pout(const char *fmt, ...){
|
Line 1229 void pout(const char *fmt, ...){
|
// initialize variable argument list |
// initialize variable argument list |
va_start(ap,fmt); |
va_start(ap,fmt); |
// in debugmode==1 mode we will print the output from the ataprint.o functions! |
// in debugmode==1 mode we will print the output from the ataprint.o functions! |
if (debugmode && debugmode!=2) | if (debugmode && debugmode != 2) { |
| FILE * f = stdout; |
#ifdef _WIN32 |
#ifdef _WIN32 |
if (facility == LOG_LOCAL1) // logging to stdout | if (facility == LOG_LOCAL1) // logging to stdout |
vfprintf(stderr,fmt,ap); | f = stderr; |
else | |
#endif |
#endif |
vprintf(fmt,ap); | vfprintf(f, fmt, ap); |
| fflush(f); |
| } |
// in debugmode==2 mode we print output from knowndrives.o functions |
// in debugmode==2 mode we print output from knowndrives.o functions |
else if (debugmode==2 || ata_debugmode || scsi_debugmode) { |
else if (debugmode==2 || ata_debugmode || scsi_debugmode) { |
openlog("smartd", LOG_PID, facility); |
openlog("smartd", LOG_PID, facility); |
Line 1322 void pout(const char *fmt, ...){
|
Line 1245 void pout(const char *fmt, ...){
|
closelog(); |
closelog(); |
} |
} |
va_end(ap); |
va_end(ap); |
fflush(NULL); |
|
return; |
return; |
} |
} |
|
|
Line 1334 static void PrintOut(int priority, const char *fmt, ..
|
Line 1256 static void PrintOut(int priority, const char *fmt, ..
|
FixGlibcTimeZoneBug(); |
FixGlibcTimeZoneBug(); |
// initialize variable argument list |
// initialize variable argument list |
va_start(ap,fmt); |
va_start(ap,fmt); |
if (debugmode) | if (debugmode) { |
| FILE * f = stdout; |
#ifdef _WIN32 |
#ifdef _WIN32 |
if (facility == LOG_LOCAL1) // logging to stdout | if (facility == LOG_LOCAL1) // logging to stdout |
vfprintf(stderr,fmt,ap); | f = stderr; |
else | |
#endif |
#endif |
vprintf(fmt,ap); | vfprintf(f, fmt, ap); |
| fflush(f); |
| } |
else { |
else { |
openlog("smartd", LOG_PID, facility); |
openlog("smartd", LOG_PID, facility); |
vsyslog_lines(priority, fmt, ap); |
vsyslog_lines(priority, fmt, ap); |
Line 1493 static void Directives()
|
Line 1417 static void Directives()
|
{ |
{ |
PrintOut(LOG_INFO, |
PrintOut(LOG_INFO, |
"Configuration file (%s) Directives (after device name):\n" |
"Configuration file (%s) Directives (after device name):\n" |
" -d TYPE Set the device type: %s, auto, removable\n" | " -d TYPE Set the device type: auto, ignore, removable,\n" |
| " %s\n" |
" -T TYPE Set the tolerance to one of: normal, permissive\n" |
" -T TYPE Set the tolerance to one of: normal, permissive\n" |
" -o VAL Enable/disable automatic offline tests (on/off)\n" |
" -o VAL Enable/disable automatic offline tests (on/off)\n" |
" -S VAL Enable/disable attribute autosave (on/off)\n" |
" -S VAL Enable/disable attribute autosave (on/off)\n" |
" -n MODE No check if: never, sleep[,N][,q], standby[,N][,q], idle[,N][,q]\n" |
" -n MODE No check if: never, sleep[,N][,q], standby[,N][,q], idle[,N][,q]\n" |
" -H Monitor SMART Health Status, report if failed\n" |
" -H Monitor SMART Health Status, report if failed\n" |
" -s REG Do Self-Test at time(s) given by regular expression REG\n" |
" -s REG Do Self-Test at time(s) given by regular expression REG\n" |
" -l TYPE Monitor SMART log or self-test status\n" | " -l TYPE Monitor SMART log or self-test status:\n" |
" Type is one of: error, selftest, xerror, offlinests, selfteststs\n" | " error, selftest, xerror, offlinests[,ns], selfteststs[,ns]\n" |
" -l scterc,R,W Set SCT Error Recovery Control\n" |
" -l scterc,R,W Set SCT Error Recovery Control\n" |
|
" -e Change device setting: aam,[N|off], apm,[N|off], lookahead,[on|off],\n" |
|
" security-freeze, standby,[N|off], wcache,[on|off]\n" |
" -f Monitor 'Usage' Attributes, report failures\n" |
" -f Monitor 'Usage' Attributes, report failures\n" |
" -m ADD Send email warning to address ADD\n" |
" -m ADD Send email warning to address ADD\n" |
" -M TYPE Modify email warning behavior (see man page)\n" |
" -M TYPE Modify email warning behavior (see man page)\n" |
Line 1519 static void Directives()
|
Line 1446 static void Directives()
|
" -v N,ST Modifies labeling of Attribute N (see man page) \n" |
" -v N,ST Modifies labeling of Attribute N (see man page) \n" |
" -P TYPE Drive-specific presets: use, ignore, show, showall\n" |
" -P TYPE Drive-specific presets: use, ignore, show, showall\n" |
" -a Default: -H -f -t -l error -l selftest -l selfteststs -C 197 -U 198\n" |
" -a Default: -H -f -t -l error -l selftest -l selfteststs -C 197 -U 198\n" |
" -F TYPE Firmware bug workaround: none, samsung, samsung2, samsung3\n" | " -F TYPE Use firmware bug workaround:\n" |
| " %s\n" |
" # Comment: text after a hash sign is ignored\n" |
" # Comment: text after a hash sign is ignored\n" |
" \\ Line continuation character\n" |
" \\ Line continuation character\n" |
"Attribute ID is a decimal integer 1 <= ID <= 255\n" |
"Attribute ID is a decimal integer 1 <= ID <= 255\n" |
"Use ID = 0 to turn off -C and/or -U Directives\n" | "Use ID = 0 to turn off -C and/or -U Directives\n" |
"Example: /dev/hda -a\n", | "Example: /dev/sda -a\n", |
configfile, smi()->get_valid_dev_types_str().c_str()); | configfile, |
return; | smi()->get_valid_dev_types_str().c_str(), |
| get_valid_firmwarebug_args()); |
} |
} |
|
|
/* Returns a pointer to a static string containing a formatted list of the valid |
/* Returns a pointer to a static string containing a formatted list of the valid |
Line 1547 static const char *GetValidArgList(char opt)
|
Line 1476 static const char *GetValidArgList(char opt)
|
return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; |
return "ioctl[,N], ataioctl[,N], scsiioctl[,N]"; |
case 'B': |
case 'B': |
case 'p': |
case 'p': |
|
case 'w': |
return "<FILE_NAME>"; |
return "<FILE_NAME>"; |
case 'i': |
case 'i': |
return "<INTEGER_SECONDS>"; |
return "<INTEGER_SECONDS>"; |
Line 1611 static void Usage()
|
Line 1541 static void Usage()
|
PrintOut(LOG_INFO," [default is "SMARTMONTOOLS_SAVESTATES"MODEL-SERIAL.TYPE.state]\n"); |
PrintOut(LOG_INFO," [default is "SMARTMONTOOLS_SAVESTATES"MODEL-SERIAL.TYPE.state]\n"); |
#endif |
#endif |
PrintOut(LOG_INFO,"\n"); |
PrintOut(LOG_INFO,"\n"); |
|
PrintOut(LOG_INFO," -w NAME, --warnexec=NAME\n"); |
|
PrintOut(LOG_INFO," Run executable NAME on warnings\n"); |
|
#ifndef _WIN32 |
|
PrintOut(LOG_INFO," [default is "SMARTMONTOOLS_SYSCONFDIR"/smartd_warning.sh]\n\n"); |
|
#else |
|
PrintOut(LOG_INFO," [default is %s/smartd_warning.cmd]\n\n", get_exe_dir().c_str()); |
|
#endif |
#ifdef _WIN32 |
#ifdef _WIN32 |
PrintOut(LOG_INFO," --service\n"); |
PrintOut(LOG_INFO," --service\n"); |
PrintOut(LOG_INFO," Running as windows service (see man page), install with:\n"); |
PrintOut(LOG_INFO," Running as windows service (see man page), install with:\n"); |
Line 1643 static bool not_allowed_in_filename(char c)
|
Line 1580 static bool not_allowed_in_filename(char c)
|
// Read error count from Summary or Extended Comprehensive SMART error log |
// Read error count from Summary or Extended Comprehensive SMART error log |
// Return -1 on error |
// Return -1 on error |
static int read_ata_error_count(ata_device * device, const char * name, |
static int read_ata_error_count(ata_device * device, const char * name, |
unsigned char fix_firmwarebug, bool extended) | firmwarebug_defs firmwarebugs, bool extended) |
{ |
{ |
if (!extended) { |
if (!extended) { |
ata_smart_errorlog log; |
ata_smart_errorlog log; |
if (ataReadErrorLog(device, &log, fix_firmwarebug)){ | if (ataReadErrorLog(device, &log, firmwarebugs)){ |
PrintOut(LOG_INFO,"Device: %s, Read Summary SMART Error Log failed\n",name); |
PrintOut(LOG_INFO,"Device: %s, Read Summary SMART Error Log failed\n",name); |
return -1; |
return -1; |
} |
} |
Line 1655 static int read_ata_error_count(ata_device * device, c
|
Line 1592 static int read_ata_error_count(ata_device * device, c
|
} |
} |
else { |
else { |
ata_smart_exterrlog logx; |
ata_smart_exterrlog logx; |
if (!ataReadExtErrorLog(device, &logx, 1 /*first sector only*/)) { | if (!ataReadExtErrorLog(device, &logx, 1 /*first sector only*/, firmwarebugs)) { |
PrintOut(LOG_INFO,"Device: %s, Read Extended Comprehensive SMART Error Log failed\n",name); |
PrintOut(LOG_INFO,"Device: %s, Read Extended Comprehensive SMART Error Log failed\n",name); |
return -1; |
return -1; |
} |
} |
Line 1667 static int read_ata_error_count(ata_device * device, c
|
Line 1604 static int read_ata_error_count(ata_device * device, c
|
// returns <0 if problem. Otherwise, bottom 8 bits are the self test |
// returns <0 if problem. Otherwise, bottom 8 bits are the self test |
// error count, and top bits are the power-on hours of the last error. |
// error count, and top bits are the power-on hours of the last error. |
static int SelfTestErrorCount(ata_device * device, const char * name, |
static int SelfTestErrorCount(ata_device * device, const char * name, |
unsigned char fix_firmwarebug) | firmwarebug_defs firmwarebugs) |
{ |
{ |
struct ata_smart_selftestlog log; |
struct ata_smart_selftestlog log; |
|
|
if (ataReadSelfTestLog(device, &log, fix_firmwarebug)){ | if (ataReadSelfTestLog(device, &log, firmwarebugs)){ |
PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name); |
PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name); |
return -1; |
return -1; |
} |
} |
|
|
// return current number of self-test errors |
// return current number of self-test errors |
return ataPrintSmartSelfTestlog(&log, false, fix_firmwarebug); | return ataPrintSmartSelfTestlog(&log, false, firmwarebugs); |
} |
} |
|
|
#define SELFTEST_ERRORCOUNT(x) (x & 0xff) |
#define SELFTEST_ERRORCOUNT(x) (x & 0xff) |
#define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff) |
#define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff) |
|
|
|
// Check offline data collection status |
|
static inline bool is_offl_coll_in_progress(unsigned char status) |
|
{ |
|
return ((status & 0x7f) == 0x03); |
|
} |
|
|
|
// Check self-test execution status |
|
static inline bool is_self_test_in_progress(unsigned char status) |
|
{ |
|
return ((status >> 4) == 0xf); |
|
} |
|
|
// Log offline data collection status |
// Log offline data collection status |
static void log_offline_data_coll_status(const char * name, unsigned char status) |
static void log_offline_data_coll_status(const char * name, unsigned char status) |
{ |
{ |
Line 1775 static void finish_device_scan(dev_config & cfg, dev_s
|
Line 1724 static void finish_device_scan(dev_config & cfg, dev_s
|
state.scheduled_test_next_check = time(0); |
state.scheduled_test_next_check = time(0); |
} |
} |
|
|
|
// Common function to format result message for ATA setting |
|
static void format_set_result_msg(std::string & msg, const char * name, bool ok, |
|
int set_option = 0, bool has_value = false) |
|
{ |
|
if (!msg.empty()) |
|
msg += ", "; |
|
msg += name; |
|
if (!ok) |
|
msg += ":--"; |
|
else if (set_option < 0) |
|
msg += ":off"; |
|
else if (has_value) |
|
msg += strprintf(":%d", set_option-1); |
|
else if (set_option > 0) |
|
msg += ":on"; |
|
} |
|
|
|
|
// TODO: Add '-F swapid' directive |
// TODO: Add '-F swapid' directive |
const bool fix_swapped_id = false; |
const bool fix_swapped_id = false; |
|
|
Line 1801 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 1767 static int ATADeviceScan(dev_config & cfg, dev_state &
|
return 2; |
return 2; |
} |
} |
|
|
// Log drive identity and size | // Get drive identity, size and rotation rate (HDD/SSD) |
char model[40+1], serial[20+1], firmware[8+1]; |
char model[40+1], serial[20+1], firmware[8+1]; |
ata_format_id_string(model, drive.model, sizeof(model)-1); |
ata_format_id_string(model, drive.model, sizeof(model)-1); |
ata_format_id_string(serial, drive.serial_no, sizeof(serial)-1); |
ata_format_id_string(serial, drive.serial_no, sizeof(serial)-1); |
Line 1810 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 1776 static int ATADeviceScan(dev_config & cfg, dev_state &
|
ata_size_info sizes; |
ata_size_info sizes; |
ata_get_size_info(&drive, sizes); |
ata_get_size_info(&drive, sizes); |
state.num_sectors = sizes.sectors; |
state.num_sectors = sizes.sectors; |
|
cfg.dev_rpm = ata_get_rotation_rate(&drive); |
|
|
char wwn[30]; wwn[0] = 0; |
char wwn[30]; wwn[0] = 0; |
unsigned oui = 0; uint64_t unique_id = 0; |
unsigned oui = 0; uint64_t unique_id = 0; |
Line 1817 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 1784 static int ATADeviceScan(dev_config & cfg, dev_state &
|
if (naa >= 0) |
if (naa >= 0) |
snprintf(wwn, sizeof(wwn), "WWN:%x-%06x-%09"PRIx64", ", naa, oui, unique_id); |
snprintf(wwn, sizeof(wwn), "WWN:%x-%06x-%09"PRIx64", ", naa, oui, unique_id); |
|
|
|
// Format device id string for warning emails |
char cap[32]; |
char cap[32]; |
PrintOut(LOG_INFO, "Device: %s, %s, S/N:%s, %sFW:%s, %s\n", name, | cfg.dev_idinfo = strprintf("%s, S/N:%s, %sFW:%s, %s", model, serial, wwn, firmware, |
model, serial, wwn, firmware, | format_capacity(cap, sizeof(cap), sizes.capacity, ".")); |
format_capacity(cap, sizeof(cap), sizes.capacity, ".")); | |
|
|
|
PrintOut(LOG_INFO, "Device: %s, %s\n", name, cfg.dev_idinfo.c_str()); |
|
|
// Show if device in database, and use preset vendor attribute |
// Show if device in database, and use preset vendor attribute |
// options unless user has requested otherwise. |
// options unless user has requested otherwise. |
if (cfg.ignorepresets) |
if (cfg.ignorepresets) |
Line 1829 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 1798 static int ATADeviceScan(dev_config & cfg, dev_state &
|
else { |
else { |
// Apply vendor specific presets, print warning if present |
// Apply vendor specific presets, print warning if present |
const drive_settings * dbentry = lookup_drive_apply_presets( |
const drive_settings * dbentry = lookup_drive_apply_presets( |
&drive, cfg.attribute_defs, cfg.fix_firmwarebug); | &drive, cfg.attribute_defs, cfg.firmwarebugs); |
if (!dbentry) |
if (!dbentry) |
PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name); |
PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name); |
else { |
else { |
Line 1953 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 1922 static int ATADeviceScan(dev_config & cfg, dev_state &
|
|
|
if ( (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) |
if ( (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) |
&& !ata_return_temperature_value(&state.smartval, cfg.attribute_defs)) { |
&& !ata_return_temperature_value(&state.smartval, cfg.attribute_defs)) { |
PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", name); | PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n", |
| name, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit); |
cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; |
cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; |
} |
} |
|
|
|
// Report ignored '-r' or '-R' directives |
|
for (int id = 1; id <= 255; id++) { |
|
if (cfg.monitor_attr_flags.is_set(id, MONITOR_RAW_PRINT)) { |
|
char opt = (!cfg.monitor_attr_flags.is_set(id, MONITOR_RAW) ? 'r' : 'R'); |
|
const char * excl = (cfg.monitor_attr_flags.is_set(id, |
|
(opt == 'r' ? MONITOR_AS_CRIT : MONITOR_RAW_AS_CRIT)) ? "!" : ""); |
|
|
|
int idx = ata_find_attr_index(id, state.smartval); |
|
if (idx < 0) |
|
PrintOut(LOG_INFO,"Device: %s, no Attribute %d, ignoring -%c %d%s\n", name, id, opt, id, excl); |
|
else { |
|
bool prefail = !!ATTRIBUTE_FLAGS_PREFAILURE(state.smartval.vendor_attributes[idx].flags); |
|
if (!((prefail && cfg.prefail) || (!prefail && cfg.usage))) |
|
PrintOut(LOG_INFO,"Device: %s, not monitoring %s Attributes, ignoring -%c %d%s\n", name, |
|
(prefail ? "Prefailure" : "Usage"), opt, id, excl); |
|
} |
|
} |
|
} |
} |
} |
|
|
// enable/disable automatic on-line testing |
// enable/disable automatic on-line testing |
Line 1981 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 1970 static int ATADeviceScan(dev_config & cfg, dev_state &
|
bool smart_logdir_ok = false, gp_logdir_ok = false; |
bool smart_logdir_ok = false, gp_logdir_ok = false; |
|
|
if ( isGeneralPurposeLoggingCapable(&drive) |
if ( isGeneralPurposeLoggingCapable(&drive) |
&& (cfg.errorlog || cfg.selftest) ) { | && (cfg.errorlog || cfg.selftest) |
| && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) { |
if (!ataReadLogDirectory(atadev, &smart_logdir, false)) |
if (!ataReadLogDirectory(atadev, &smart_logdir, false)) |
smart_logdir_ok = true; |
smart_logdir_ok = true; |
} |
} |
|
|
if (cfg.xerrorlog) { | if (cfg.xerrorlog && !cfg.firmwarebugs.is_set(BUG_NOLOGDIR)) { |
if (!ataReadLogDirectory(atadev, &gp_logdir, true)) |
if (!ataReadLogDirectory(atadev, &gp_logdir, true)) |
gp_logdir_ok = true; |
gp_logdir_ok = true; |
} |
} |
Line 2001 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 1991 static int ATADeviceScan(dev_config & cfg, dev_state &
|
PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest (override with -T permissive)\n", name); |
PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest (override with -T permissive)\n", name); |
cfg.selftest = false; |
cfg.selftest = false; |
} |
} |
else if ((retval = SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug)) < 0) { | else if ((retval = SelfTestErrorCount(atadev, name, cfg.firmwarebugs)) < 0) { |
PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest\n", name); |
PrintOut(LOG_INFO, "Device: %s, no SMART Self-test Log, ignoring -l selftest\n", name); |
cfg.selftest = false; |
cfg.selftest = false; |
} |
} |
Line 2021 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 2011 static int ATADeviceScan(dev_config & cfg, dev_state &
|
PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error (override with -T permissive)\n", name); |
PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error (override with -T permissive)\n", name); |
cfg.errorlog = false; |
cfg.errorlog = false; |
} |
} |
else if ((errcnt1 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, false)) < 0) { | else if ((errcnt1 = read_ata_error_count(atadev, name, cfg.firmwarebugs, false)) < 0) { |
PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error\n", name); |
PrintOut(LOG_INFO, "Device: %s, no SMART Error Log, ignoring -l error\n", name); |
cfg.errorlog = false; |
cfg.errorlog = false; |
} |
} |
Line 2031 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 2021 static int ATADeviceScan(dev_config & cfg, dev_state &
|
|
|
if (cfg.xerrorlog) { |
if (cfg.xerrorlog) { |
int errcnt2; |
int errcnt2; |
if (!(cfg.permissive || (gp_logdir_ok && gp_logdir.entry[0x03-1].numsectors))) { | if (!( cfg.permissive || cfg.firmwarebugs.is_set(BUG_NOLOGDIR) |
| || (gp_logdir_ok && gp_logdir.entry[0x03-1].numsectors) )) { |
PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror (override with -T permissive)\n", |
PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror (override with -T permissive)\n", |
name); |
name); |
cfg.xerrorlog = false; |
cfg.xerrorlog = false; |
} |
} |
else if ((errcnt2 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, true)) < 0) { | else if ((errcnt2 = read_ata_error_count(atadev, name, cfg.firmwarebugs, true)) < 0) { |
PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror\n", name); |
PrintOut(LOG_INFO, "Device: %s, no Extended Comprehensive SMART Error Log, ignoring -l xerror\n", name); |
cfg.xerrorlog = false; |
cfg.xerrorlog = false; |
} |
} |
Line 2077 static int ATADeviceScan(dev_config & cfg, dev_state &
|
Line 2068 static int ATADeviceScan(dev_config & cfg, dev_state &
|
} |
} |
} |
} |
|
|
|
// Apply ATA settings |
|
std::string msg; |
|
|
|
if (cfg.set_aam) |
|
format_set_result_msg(msg, "AAM", (cfg.set_aam > 0 ? |
|
ata_set_features(atadev, ATA_ENABLE_AAM, cfg.set_aam-1) : |
|
ata_set_features(atadev, ATA_DISABLE_AAM)), cfg.set_aam, true); |
|
|
|
if (cfg.set_apm) |
|
format_set_result_msg(msg, "APM", (cfg.set_apm > 0 ? |
|
ata_set_features(atadev, ATA_ENABLE_APM, cfg.set_apm-1) : |
|
ata_set_features(atadev, ATA_DISABLE_APM)), cfg.set_apm, true); |
|
|
|
if (cfg.set_lookahead) |
|
format_set_result_msg(msg, "Rd-ahead", ata_set_features(atadev, |
|
(cfg.set_lookahead > 0 ? ATA_ENABLE_READ_LOOK_AHEAD : ATA_DISABLE_READ_LOOK_AHEAD)), |
|
cfg.set_lookahead); |
|
|
|
if (cfg.set_wcache) |
|
format_set_result_msg(msg, "Wr-cache", ata_set_features(atadev, |
|
(cfg.set_wcache > 0? ATA_ENABLE_WRITE_CACHE : ATA_DISABLE_WRITE_CACHE)), cfg.set_wcache); |
|
|
|
if (cfg.set_security_freeze) |
|
format_set_result_msg(msg, "Security freeze", |
|
ata_nodata_command(atadev, ATA_SECURITY_FREEZE_LOCK)); |
|
|
|
if (cfg.set_standby) |
|
format_set_result_msg(msg, "Standby", |
|
ata_nodata_command(atadev, ATA_IDLE, cfg.set_standby-1), cfg.set_standby, true); |
|
|
|
// Report as one log entry |
|
if (!msg.empty()) |
|
PrintOut(LOG_INFO, "Device: %s, ATA settings applied: %s\n", name, msg.c_str()); |
|
|
// set SCT Error Recovery Control if requested |
// set SCT Error Recovery Control if requested |
if (cfg.sct_erc_set) { |
if (cfg.sct_erc_set) { |
if (!isSCTErrorRecoveryControlCapable(&drive)) |
if (!isSCTErrorRecoveryControlCapable(&drive)) |
Line 2138 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
Line 2163 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
UINT8 tBuf[64]; |
UINT8 tBuf[64]; |
UINT8 inqBuf[96]; |
UINT8 inqBuf[96]; |
UINT8 vpdBuf[252]; |
UINT8 vpdBuf[252]; |
char lu_id[64]; | char lu_id[64], serial[256], vendor[40], model[40]; |
|
|
// Device must be open |
// Device must be open |
memset(inqBuf, 0, 96); |
memset(inqBuf, 0, 96); |
Line 2152 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
Line 2177 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
return 2; |
return 2; |
} |
} |
} |
} |
version = inqBuf[2]; | version = (inqBuf[2] & 0x7f); /* Accept old ISO/IEC 9316:1995 variants */ |
| |
avail_len = inqBuf[4] + 5; |
avail_len = inqBuf[4] + 5; |
len = (avail_len < req_len) ? avail_len : req_len; |
len = (avail_len < req_len) ? avail_len : req_len; |
// peri_dt = inqBuf[0] & 0x1f; |
|
if (len < 36) { |
if (len < 36) { |
PrintOut(LOG_INFO, "Device: %s, INQUIRY response less than 36 bytes; " |
PrintOut(LOG_INFO, "Device: %s, INQUIRY response less than 36 bytes; " |
"skip device\n", device); |
"skip device\n", device); |
return 2; |
return 2; |
} |
} |
|
|
|
int pdt = inqBuf[0] & 0x1f; |
|
|
|
if (! ((0 == pdt) || (4 == pdt) || (5 == pdt) || (7 == pdt) || |
|
(0xe == pdt))) { |
|
PrintOut(LOG_INFO, "Device: %s, not a disk like device [PDT=0x%x], " |
|
"skip\n", device, pdt); |
|
return 2; |
|
} |
|
|
|
if (supported_vpd_pages_p) { |
|
delete supported_vpd_pages_p; |
|
supported_vpd_pages_p = NULL; |
|
} |
|
supported_vpd_pages_p = new supported_vpd_pages(scsidev); |
|
|
lu_id[0] = '\0'; |
lu_id[0] = '\0'; |
if ((version >= 0x4) && (version < 0x8)) { | if ((version >= 0x3) && (version < 0x8)) { |
/* SPC-2 to SPC-5 */ | /* SPC to SPC-5 */ |
if (0 == (err = scsiInquiryVpd(scsidev, 0x83, vpdBuf, sizeof(vpdBuf)))) { | if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_DEVICE_IDENTIFICATION, |
| vpdBuf, sizeof(vpdBuf))) { |
len = vpdBuf[3]; |
len = vpdBuf[3]; |
scsi_decode_lu_dev_id(vpdBuf + 4, len, lu_id, sizeof(lu_id), NULL); |
scsi_decode_lu_dev_id(vpdBuf + 4, len, lu_id, sizeof(lu_id), NULL); |
} |
} |
} | } |
| serial[0] = '\0'; |
| if (0 == scsiInquiryVpd(scsidev, SCSI_VPD_UNIT_SERIAL_NUMBER, |
| vpdBuf, sizeof(vpdBuf))) { |
| len = vpdBuf[3]; |
| vpdBuf[4 + len] = '\0'; |
| scsi_format_id_string(serial, (const unsigned char *)&vpdBuf[4], len); |
| } |
|
|
unsigned int lb_size; |
unsigned int lb_size; |
char si_str[64]; |
char si_str[64]; |
uint64_t capacity = scsiGetSize(scsidev, &lb_size); | uint64_t capacity = scsiGetSize(scsidev, &lb_size, NULL); |
|
|
if (capacity) |
if (capacity) |
format_capacity(si_str, sizeof(si_str), capacity); |
format_capacity(si_str, sizeof(si_str), capacity); |
else |
else |
si_str[0] = '\0'; |
si_str[0] = '\0'; |
PrintOut(LOG_INFO, "Device: %s, [%.8s %.16s %.4s]%s%s%s%s\n", |
|
device, (char *)&inqBuf[8], (char *)&inqBuf[16], |
|
(char *)&inqBuf[32], |
|
(lu_id[0] ? ", lu id: " : ""), (lu_id[0] ? lu_id : ""), |
|
(si_str[0] ? ", " : ""), (si_str[0] ? si_str : "")); |
|
|
|
|
// Format device id string for warning emails |
|
cfg.dev_idinfo = strprintf("[%.8s %.16s %.4s]%s%s%s%s%s%s", |
|
(char *)&inqBuf[8], (char *)&inqBuf[16], (char *)&inqBuf[32], |
|
(lu_id[0] ? ", lu id: " : ""), (lu_id[0] ? lu_id : ""), |
|
(serial[0] ? ", S/N: " : ""), (serial[0] ? serial : ""), |
|
(si_str[0] ? ", " : ""), (si_str[0] ? si_str : "")); |
|
|
|
// format "model" string |
|
scsi_format_id_string(vendor, (const unsigned char *)&inqBuf[8], 8); |
|
scsi_format_id_string(model, (const unsigned char *)&inqBuf[16], 16); |
|
PrintOut(LOG_INFO, "Device: %s, %s\n", device, cfg.dev_idinfo.c_str()); |
|
|
// check that device is ready for commands. IE stores its stuff on |
// check that device is ready for commands. IE stores its stuff on |
// the media. |
// the media. |
if ((err = scsiTestUnitReady(scsidev))) { |
if ((err = scsiTestUnitReady(scsidev))) { |
Line 2238 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
Line 2294 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
case IE_LPAGE: |
case IE_LPAGE: |
state.SmartPageSupported = 1; |
state.SmartPageSupported = 1; |
break; |
break; |
|
case READ_ERROR_COUNTER_LPAGE: |
|
state.ReadECounterPageSupported = 1; |
|
break; |
|
case WRITE_ERROR_COUNTER_LPAGE: |
|
state.WriteECounterPageSupported = 1; |
|
break; |
|
case VERIFY_ERROR_COUNTER_LPAGE: |
|
state.VerifyECounterPageSupported = 1; |
|
break; |
|
case NON_MEDIUM_ERROR_LPAGE: |
|
state.NonMediumErrorPageSupported = 1; |
|
break; |
default: |
default: |
break; |
break; |
} |
} |
Line 2256 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
Line 2324 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device); |
PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device); |
state.SuppressReport = 1; |
state.SuppressReport = 1; |
if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) { |
if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) { |
PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", device); | PrintOut(LOG_INFO, "Device: %s, can't monitor Temperature, ignoring -W %d,%d,%d\n", |
| device, cfg.tempdiff, cfg.tempinfo, cfg.tempcrit); |
cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; |
cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0; |
} |
} |
} |
} |
Line 2298 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
Line 2367 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
// tell user we are registering device |
// tell user we are registering device |
PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device); |
PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device); |
|
|
// TODO: Build file name for state file | // Make sure that init_standby_check() ignores SCSI devices |
if (!state_path_prefix.empty()) { | cfg.offlinests_ns = cfg.selfteststs_ns = false; |
PrintOut(LOG_INFO, "Device: %s, persistence not yet supported for SCSI; ignoring -s option.\n", device); | |
} | |
// TODO: Build file name for attribute log file | |
if (!attrlog_path_prefix.empty()) { | |
PrintOut(LOG_INFO, "Device: %s, attribute log not yet supported for SCSI; ignoring -A option.\n", device); | |
} | |
|
|
// close file descriptor |
// close file descriptor |
CloseDevice(scsidev, device); |
CloseDevice(scsidev, device); |
|
|
|
if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) { |
|
// Build file name for state file |
|
std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_'); |
|
std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_'); |
|
if (!state_path_prefix.empty()) { |
|
cfg.state_file = strprintf("%s%s-%s-%s.scsi.state", state_path_prefix.c_str(), vendor, model, serial); |
|
// Read previous state |
|
if (read_dev_state(cfg.state_file.c_str(), state)) { |
|
PrintOut(LOG_INFO, "Device: %s, state read from %s\n", device, cfg.state_file.c_str()); |
|
// Copy ATA attribute values to temp state |
|
state.update_temp_state(); |
|
} |
|
} |
|
if (!attrlog_path_prefix.empty()) |
|
cfg.attrlog_file = strprintf("%s%s-%s-%s.scsi.csv", attrlog_path_prefix.c_str(), vendor, model, serial); |
|
} |
|
|
finish_device_scan(cfg, state); |
finish_device_scan(cfg, state); |
|
|
return 0; |
return 0; |
Line 2353 static void CheckSelfTestLogs(const dev_config & cfg,
|
Line 2433 static void CheckSelfTestLogs(const dev_config & cfg,
|
// new failure. |
// new failure. |
PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n", |
PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n", |
name, newh); |
name, newh); |
MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n", | MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d", |
name, newh); |
name, newh); |
state.must_write = true; |
state.must_write = true; |
} |
} |
Line 2647 static int DoATASelfTest(const dev_config & cfg, dev_s
|
Line 2727 static int DoATASelfTest(const dev_config & cfg, dev_s
|
|
|
// If currently running a self-test, do not interrupt it to start another. |
// If currently running a self-test, do not interrupt it to start another. |
if (15==(data.self_test_exec_status >> 4)) { |
if (15==(data.self_test_exec_status >> 4)) { |
if (cfg.fix_firmwarebug == FIX_SAMSUNG3 && data.self_test_exec_status == 0xf0) { | if (cfg.firmwarebugs.is_set(BUG_SAMSUNG3) && data.self_test_exec_status == 0xf0) { |
PrintOut(LOG_INFO, "Device: %s, will not skip scheduled %sTest " |
PrintOut(LOG_INFO, "Device: %s, will not skip scheduled %sTest " |
"despite unclear Self-Test byte (SAMSUNG Firmware bug).\n", name, testname); |
"despite unclear Self-Test byte (SAMSUNG Firmware bug).\n", name, testname); |
} else { |
} else { |
Line 2686 static int DoATASelfTest(const dev_config & cfg, dev_s
|
Line 2766 static int DoATASelfTest(const dev_config & cfg, dev_s
|
return retval; |
return retval; |
} |
} |
|
|
if (testtype != 'O') | // Report recent test start to do_disable_standby_check() |
// Log next self-test execution status | // and force log of next test status |
state.smartval.self_test_exec_status = 0xff; | if (testtype == 'O') |
| state.offline_started = true; |
| else |
| state.selftest_started = true; |
|
|
PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname); |
PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname); |
return 0; |
return 0; |
Line 2723 static void check_pending(const dev_config & cfg, dev_
|
Line 2806 static void check_pending(const dev_config & cfg, dev_
|
s += strprintf(" (changed %+"PRId64")", rawval - prev_rawval); |
s += strprintf(" (changed %+"PRId64")", rawval - prev_rawval); |
|
|
PrintOut(LOG_CRIT, "%s\n", s.c_str()); |
PrintOut(LOG_CRIT, "%s\n", s.c_str()); |
MailWarning(cfg, state, mailtype, "%s\n", s.c_str()); | MailWarning(cfg, state, mailtype, "%s", s.c_str()); |
state.must_write = true; |
state.must_write = true; |
} |
} |
|
|
// Format Temperature value |
// Format Temperature value |
static const char * fmt_temp(unsigned char x, char * buf) | static const char * fmt_temp(unsigned char x, char (& buf)[20]) |
{ |
{ |
if (!x) // unset |
if (!x) // unset |
strcpy(buf, "??"); | return "??"; |
else | snprintf(buf, sizeof(buf), "%u", x); |
sprintf(buf, "%u", x); | |
return buf; |
return buf; |
} |
} |
|
|
Line 2797 static void CheckTemperature(const dev_config & cfg, d
|
Line 2879 static void CheckTemperature(const dev_config & cfg, d
|
if (cfg.tempcrit && currtemp >= cfg.tempcrit) { |
if (cfg.tempcrit && currtemp >= cfg.tempcrit) { |
PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n", |
PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n", |
cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); |
cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); |
MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n", | MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)", |
cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); |
cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg); |
} |
} |
else if (cfg.tempinfo && currtemp >= cfg.tempinfo) { |
else if (cfg.tempinfo && currtemp >= cfg.tempinfo) { |
Line 2826 static void check_attribute(const dev_config & cfg, de
|
Line 2908 static void check_attribute(const dev_config & cfg, de
|
// If requested, check for usage attributes that have failed. |
// If requested, check for usage attributes that have failed. |
if ( cfg.usagefailed && attrstate == ATTRSTATE_FAILED_NOW |
if ( cfg.usagefailed && attrstate == ATTRSTATE_FAILED_NOW |
&& !cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGN_FAILUSE)) { |
&& !cfg.monitor_attr_flags.is_set(attr.id, MONITOR_IGN_FAILUSE)) { |
std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs); | std::string attrname = ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm); |
PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", cfg.name.c_str(), attr.id, attrname.c_str()); |
PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %d %s.\n", cfg.name.c_str(), attr.id, attrname.c_str()); |
MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", cfg.name.c_str(), attr.id, attrname.c_str()); |
MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %d %s.", cfg.name.c_str(), attr.id, attrname.c_str()); |
state.must_write = true; |
state.must_write = true; |
Line 2893 static void check_attribute(const dev_config & cfg, de
|
Line 2975 static void check_attribute(const dev_config & cfg, de
|
// Format message |
// Format message |
std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %s to %s", |
std::string msg = strprintf("Device: %s, SMART %s Attribute: %d %s changed from %s to %s", |
cfg.name.c_str(), (prefail ? "Prefailure" : "Usage"), attr.id, |
cfg.name.c_str(), (prefail ? "Prefailure" : "Usage"), attr.id, |
ata_get_smart_attr_name(attr.id, cfg.attribute_defs).c_str(), | ata_get_smart_attr_name(attr.id, cfg.attribute_defs, cfg.dev_rpm).c_str(), |
prevstr.c_str(), currstr.c_str()); |
prevstr.c_str(), currstr.c_str()); |
|
|
// Report this change as critical ? |
// Report this change as critical ? |
Line 3063 static int ATACheckDevice(const dev_config & cfg, dev_
|
Line 3145 static int ATACheckDevice(const dev_config & cfg, dev_
|
if (cfg.offlinests) { |
if (cfg.offlinests) { |
if ( curval.offline_data_collection_status |
if ( curval.offline_data_collection_status |
!= state.smartval.offline_data_collection_status |
!= state.smartval.offline_data_collection_status |
|
|| state.offline_started // test was started in previous call |
|| (firstpass && (debugmode || (curval.offline_data_collection_status & 0x7d)))) |
|| (firstpass && (debugmode || (curval.offline_data_collection_status & 0x7d)))) |
log_offline_data_coll_status(name, curval.offline_data_collection_status); |
log_offline_data_coll_status(name, curval.offline_data_collection_status); |
} |
} |
Line 3070 static int ATACheckDevice(const dev_config & cfg, dev_
|
Line 3153 static int ATACheckDevice(const dev_config & cfg, dev_
|
// Log changes of self-test execution status |
// Log changes of self-test execution status |
if (cfg.selfteststs) { |
if (cfg.selfteststs) { |
if ( curval.self_test_exec_status != state.smartval.self_test_exec_status |
if ( curval.self_test_exec_status != state.smartval.self_test_exec_status |
|
|| state.selftest_started // test was started in previous call |
|| (firstpass && (debugmode || curval.self_test_exec_status != 0x00))) |
|| (firstpass && (debugmode || curval.self_test_exec_status != 0x00))) |
log_self_test_exec_status(name, curval.self_test_exec_status); |
log_self_test_exec_status(name, curval.self_test_exec_status); |
} |
} |
Line 3078 static int ATACheckDevice(const dev_config & cfg, dev_
|
Line 3162 static int ATACheckDevice(const dev_config & cfg, dev_
|
state.smartval = curval; |
state.smartval = curval; |
} |
} |
} |
} |
|
state.offline_started = state.selftest_started = false; |
|
|
// check if number of selftest errors has increased (note: may also DECREASE) |
// check if number of selftest errors has increased (note: may also DECREASE) |
if (cfg.selftest) |
if (cfg.selftest) |
CheckSelfTestLogs(cfg, state, SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug)); | CheckSelfTestLogs(cfg, state, SelfTestErrorCount(atadev, name, cfg.firmwarebugs)); |
|
|
// check if number of ATA errors has increased |
// check if number of ATA errors has increased |
if (cfg.errorlog || cfg.xerrorlog) { |
if (cfg.errorlog || cfg.xerrorlog) { |
|
|
int errcnt1 = -1, errcnt2 = -1; |
int errcnt1 = -1, errcnt2 = -1; |
if (cfg.errorlog) |
if (cfg.errorlog) |
errcnt1 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, false); | errcnt1 = read_ata_error_count(atadev, name, cfg.firmwarebugs, false); |
if (cfg.xerrorlog) |
if (cfg.xerrorlog) |
errcnt2 = read_ata_error_count(atadev, name, cfg.fix_firmwarebug, true); | errcnt2 = read_ata_error_count(atadev, name, cfg.firmwarebugs, true); |
|
|
// new number of errors is max of both logs |
// new number of errors is max of both logs |
int newc = (errcnt1 >= errcnt2 ? errcnt1 : errcnt2); |
int newc = (errcnt1 >= errcnt2 ? errcnt1 : errcnt2); |
Line 3137 static int SCSICheckDevice(const dev_config & cfg, dev
|
Line 3222 static int SCSICheckDevice(const dev_config & cfg, dev
|
UINT8 asc, ascq; |
UINT8 asc, ascq; |
UINT8 currenttemp; |
UINT8 currenttemp; |
UINT8 triptemp; |
UINT8 triptemp; |
|
UINT8 tBuf[252]; |
const char * name = cfg.name.c_str(); |
const char * name = cfg.name.c_str(); |
const char *cp; |
const char *cp; |
|
|
Line 3152 static int SCSICheckDevice(const dev_config & cfg, dev
|
Line 3238 static int SCSICheckDevice(const dev_config & cfg, dev
|
return 1; |
return 1; |
} else if (debugmode) |
} else if (debugmode) |
PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name); |
PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name); |
|
reset_warning_mail(cfg, state, 9, "open device worked again"); |
currenttemp = 0; |
currenttemp = 0; |
asc = 0; |
asc = 0; |
ascq = 0; |
ascq = 0; |
Line 3169 static int SCSICheckDevice(const dev_config & cfg, dev
|
Line 3256 static int SCSICheckDevice(const dev_config & cfg, dev
|
if (cp) { |
if (cp) { |
PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp); |
PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp); |
MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp); |
MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp); |
|
} else if (asc == 4 && ascq == 9) { |
|
PrintOut(LOG_INFO,"Device: %s, self-test in progress\n", name); |
} else if (debugmode) |
} else if (debugmode) |
PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n", |
PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n", |
name, (int)asc, (int)ascq); |
name, (int)asc, (int)ascq); |
Line 3176 static int SCSICheckDevice(const dev_config & cfg, dev
|
Line 3265 static int SCSICheckDevice(const dev_config & cfg, dev
|
PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name); |
PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name); |
|
|
// check temperature limits |
// check temperature limits |
if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit) | if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit || !cfg.attrlog_file.empty()) |
CheckTemperature(cfg, state, currenttemp, triptemp); |
CheckTemperature(cfg, state, currenttemp, triptemp); |
|
|
// check if number of selftest errors has increased (note: may also DECREASE) |
// check if number of selftest errors has increased (note: may also DECREASE) |
Line 3188 static int SCSICheckDevice(const dev_config & cfg, dev
|
Line 3277 static int SCSICheckDevice(const dev_config & cfg, dev
|
if (testtype) |
if (testtype) |
DoSCSISelfTest(cfg, state, scsidev, testtype); |
DoSCSISelfTest(cfg, state, scsidev, testtype); |
} |
} |
|
if (!cfg.attrlog_file.empty()){ |
|
// saving error counters to state |
|
if (state.ReadECounterPageSupported && (0 == scsiLogSense(scsidev, |
|
READ_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { |
|
scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[0].errCounter); |
|
state.scsi_error_counters[0].found=1; |
|
} |
|
if (state.WriteECounterPageSupported && (0 == scsiLogSense(scsidev, |
|
WRITE_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { |
|
scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[1].errCounter); |
|
state.scsi_error_counters[1].found=1; |
|
} |
|
if (state.VerifyECounterPageSupported && (0 == scsiLogSense(scsidev, |
|
VERIFY_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { |
|
scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[2].errCounter); |
|
state.scsi_error_counters[2].found=1; |
|
} |
|
if (state.NonMediumErrorPageSupported && (0 == scsiLogSense(scsidev, |
|
NON_MEDIUM_ERROR_LPAGE, 0, tBuf, sizeof(tBuf), 0))) { |
|
scsiDecodeNonMediumErrPage(tBuf, &state.scsi_nonmedium_error.nme); |
|
state.scsi_nonmedium_error.found=1; |
|
} |
|
} |
CloseDevice(scsidev, name); |
CloseDevice(scsidev, name); |
return 0; |
return 0; |
} |
} |
|
|
|
// 0=not used, 1=not disabled, 2=disable rejected by OS, 3=disabled |
|
static int standby_disable_state = 0; |
|
|
|
static void init_disable_standby_check(dev_config_vector & configs) |
|
{ |
|
// Check for '-l offlinests,ns' or '-l selfteststs,ns' directives |
|
bool sts1 = false, sts2 = false; |
|
for (unsigned i = 0; i < configs.size() && !(sts1 || sts2); i++) { |
|
const dev_config & cfg = configs.at(i); |
|
if (cfg.offlinests_ns) |
|
sts1 = true; |
|
if (cfg.selfteststs_ns) |
|
sts2 = true; |
|
} |
|
|
|
// Check for support of disable auto standby |
|
// Reenable standby if smartd.conf was reread |
|
if (sts1 || sts2 || standby_disable_state == 3) { |
|
if (!smi()->disable_system_auto_standby(false)) { |
|
if (standby_disable_state == 3) |
|
PrintOut(LOG_CRIT, "System auto standby enable failed: %s\n", smi()->get_errmsg()); |
|
if (sts1 || sts2) { |
|
PrintOut(LOG_INFO, "Disable auto standby not supported, ignoring ',ns' from %s%s%s\n", |
|
(sts1 ? "-l offlinests,ns" : ""), (sts1 && sts2 ? " and " : ""), (sts2 ? "-l selfteststs,ns" : "")); |
|
sts1 = sts2 = false; |
|
} |
|
} |
|
} |
|
|
|
standby_disable_state = (sts1 || sts2 ? 1 : 0); |
|
} |
|
|
|
static void do_disable_standby_check(const dev_config_vector & configs, const dev_state_vector & states) |
|
{ |
|
if (!standby_disable_state) |
|
return; |
|
|
|
// Check for just started or still running self-tests |
|
bool running = false; |
|
for (unsigned i = 0; i < configs.size() && !running; i++) { |
|
const dev_config & cfg = configs.at(i); const dev_state & state = states.at(i); |
|
|
|
if ( ( cfg.offlinests_ns |
|
&& (state.offline_started || |
|
is_offl_coll_in_progress(state.smartval.offline_data_collection_status))) |
|
|| ( cfg.selfteststs_ns |
|
&& (state.selftest_started || |
|
is_self_test_in_progress(state.smartval.self_test_exec_status))) ) |
|
running = true; |
|
// state.offline/selftest_started will be reset after next logging of test status |
|
} |
|
|
|
// Disable/enable auto standby and log state changes |
|
if (!running) { |
|
if (standby_disable_state != 1) { |
|
if (!smi()->disable_system_auto_standby(false)) |
|
PrintOut(LOG_CRIT, "Self-test(s) completed, system auto standby enable failed: %s\n", |
|
smi()->get_errmsg()); |
|
else |
|
PrintOut(LOG_INFO, "Self-test(s) completed, system auto standby enabled\n"); |
|
standby_disable_state = 1; |
|
} |
|
} |
|
else if (!smi()->disable_system_auto_standby(true)) { |
|
if (standby_disable_state != 2) { |
|
PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disable rejected: %s\n", |
|
smi()->get_errmsg()); |
|
standby_disable_state = 2; |
|
} |
|
} |
|
else { |
|
if (standby_disable_state != 3) { |
|
PrintOut(LOG_INFO, "Self-test(s) in progress, system auto standby disabled\n"); |
|
standby_disable_state = 3; |
|
} |
|
} |
|
} |
|
|
// Checks the SMART status of all ATA and SCSI devices |
// Checks the SMART status of all ATA and SCSI devices |
static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector & states, |
static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector & states, |
smart_device_list & devices, bool firstpass, bool allow_selftests) |
smart_device_list & devices, bool firstpass, bool allow_selftests) |
Line 3205 static void CheckDevicesOnce(const dev_config_vector &
|
Line 3395 static void CheckDevicesOnce(const dev_config_vector &
|
else if (dev->is_scsi()) |
else if (dev->is_scsi()) |
SCSICheckDevice(cfg, state, dev->to_scsi(), allow_selftests); |
SCSICheckDevice(cfg, state, dev->to_scsi(), allow_selftests); |
} |
} |
|
|
|
do_disable_standby_check(configs, states); |
} |
} |
|
|
// Set if Initialize() was called |
// Set if Initialize() was called |
Line 3367 static void printoutvaliddirectiveargs(int priority, c
|
Line 3559 static void printoutvaliddirectiveargs(int priority, c
|
PrintOut(priority, "use, ignore, show, showall"); |
PrintOut(priority, "use, ignore, show, showall"); |
break; |
break; |
case 'F': |
case 'F': |
PrintOut(priority, "none, samsung, samsung2, samsung3"); | PrintOut(priority, "%s", get_valid_firmwarebug_args()); |
break; |
break; |
|
case 'e': |
|
PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], " |
|
"security-freeze, standby,[N|off], wcache,[on|off]"); |
|
break; |
} |
} |
} |
} |
|
|
Line 3430 static int Get3Integers(const char *arg, const char *n
|
Line 3626 static int Get3Integers(const char *arg, const char *n
|
} |
} |
|
|
|
|
|
#ifdef _WIN32 |
|
|
|
// Concatenate strtok() results if quoted with "..." |
|
static const char * strtok_dequote(const char * delimiters) |
|
{ |
|
const char * t = strtok(0, delimiters); |
|
if (!t || t[0] != '"') |
|
return t; |
|
|
|
static std::string token; |
|
token = t+1; |
|
for (;;) { |
|
t = strtok(0, delimiters); |
|
if (!t || !*t) |
|
return "\""; |
|
token += ' '; |
|
int len = strlen(t); |
|
if (t[len-1] == '"') { |
|
token += std::string(t, len-1); |
|
break; |
|
} |
|
token += t; |
|
} |
|
return token.c_str(); |
|
} |
|
|
|
#endif // _WIN32 |
|
|
|
|
// This function returns 1 if it has correctly parsed one token (and |
// This function returns 1 if it has correctly parsed one token (and |
// any arguments), else zero if no tokens remain. It returns -1 if an |
// any arguments), else zero if no tokens remain. It returns -1 if an |
// error was encountered. |
// error was encountered. |
Line 3499 static int ParseToken(char * token, dev_config & cfg)
|
Line 3724 static int ParseToken(char * token, dev_config & cfg)
|
// specify the device type |
// specify the device type |
if ((arg = strtok(NULL, delim)) == NULL) { |
if ((arg = strtok(NULL, delim)) == NULL) { |
missingarg = 1; |
missingarg = 1; |
|
} else if (!strcmp(arg, "ignore")) { |
|
cfg.ignore = true; |
} else if (!strcmp(arg, "removable")) { |
} else if (!strcmp(arg, "removable")) { |
cfg.removable = true; |
cfg.removable = true; |
} else if (!strcmp(arg, "auto")) { |
} else if (!strcmp(arg, "auto")) { |
Line 3509 static int ParseToken(char * token, dev_config & cfg)
|
Line 3736 static int ParseToken(char * token, dev_config & cfg)
|
break; |
break; |
case 'F': |
case 'F': |
// fix firmware bug |
// fix firmware bug |
if ((arg = strtok(NULL, delim)) == NULL) { | if (!(arg = strtok(0, delim))) |
missingarg = 1; |
missingarg = 1; |
} else if (!strcmp(arg, "none")) { | else if (!parse_firmwarebug_def(arg, cfg.firmwarebugs)) |
cfg.fix_firmwarebug = FIX_NONE; | |
} else if (!strcmp(arg, "samsung")) { | |
cfg.fix_firmwarebug = FIX_SAMSUNG; | |
} else if (!strcmp(arg, "samsung2")) { | |
cfg.fix_firmwarebug = FIX_SAMSUNG2; | |
} else if (!strcmp(arg, "samsung3")) { | |
cfg.fix_firmwarebug = FIX_SAMSUNG3; | |
} else { | |
badarg = 1; |
badarg = 1; |
} |
|
break; |
break; |
case 'H': |
case 'H': |
// check SMART status |
// check SMART status |
Line 3560 static int ParseToken(char * token, dev_config & cfg)
|
Line 3778 static int ParseToken(char * token, dev_config & cfg)
|
} else if (!strcmp(arg, "offlinests")) { |
} else if (!strcmp(arg, "offlinests")) { |
// track changes in offline data collection status |
// track changes in offline data collection status |
cfg.offlinests = true; |
cfg.offlinests = true; |
|
} else if (!strcmp(arg, "offlinests,ns")) { |
|
// track changes in offline data collection status, disable auto standby |
|
cfg.offlinests = cfg.offlinests_ns = true; |
} else if (!strcmp(arg, "selfteststs")) { |
} else if (!strcmp(arg, "selfteststs")) { |
// track changes in self-test execution status |
// track changes in self-test execution status |
cfg.selfteststs = true; |
cfg.selfteststs = true; |
|
} else if (!strcmp(arg, "selfteststs,ns")) { |
|
// track changes in self-test execution status, disable auto standby |
|
cfg.selfteststs = cfg.selfteststs_ns = true; |
} else if (!strncmp(arg, "scterc,", sizeof("scterc,")-1)) { |
} else if (!strncmp(arg, "scterc,", sizeof("scterc,")-1)) { |
// set SCT Error Recovery Control |
// set SCT Error Recovery Control |
unsigned rt = ~0, wt = ~0; int nc = -1; |
unsigned rt = ~0, wt = ~0; int nc = -1; |
Line 3691 static int ParseToken(char * token, dev_config & cfg)
|
Line 3915 static int ParseToken(char * token, dev_config & cfg)
|
if (!cfg.emailaddress.empty()) |
if (!cfg.emailaddress.empty()) |
PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n", |
PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n", |
configfile, lineno, name, cfg.emailaddress.c_str()); |
configfile, lineno, name, cfg.emailaddress.c_str()); |
|
#ifdef _WIN32 |
|
if ( !strcmp(arg, "msgbox") || !strcmp(arg, "sysmsgbox") |
|
|| str_starts_with(arg, "msgbox,") || str_starts_with(arg, "sysmsgbox,")) { |
|
cfg.emailaddress = "console"; |
|
const char * arg2 = strchr(arg, ','); |
|
if (arg2) |
|
cfg.emailaddress += arg2; |
|
PrintOut(LOG_INFO, "File %s line %d (drive %s): Deprecated -m %s changed to -m %s\n", |
|
configfile, lineno, name, arg, cfg.emailaddress.c_str()); |
|
} |
|
else |
|
#endif |
cfg.emailaddress = arg; |
cfg.emailaddress = arg; |
} |
} |
break; |
break; |
Line 3708 static int ParseToken(char * token, dev_config & cfg)
|
Line 3944 static int ParseToken(char * token, dev_config & cfg)
|
cfg.emailtest = 1; |
cfg.emailtest = 1; |
else if (!strcmp(arg, "exec")) { |
else if (!strcmp(arg, "exec")) { |
// Get the next argument (the command line) |
// Get the next argument (the command line) |
if (!(arg = strtok(NULL, delim))) { | #ifdef _WIN32 |
| // Allow "/path name/with spaces/..." on Windows |
| arg = strtok_dequote(delim); |
| if (arg && arg[0] == '"') { |
| PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument: missing closing quote\n", |
| configfile, lineno, name, token); |
| return -1; |
| } |
| #else |
| arg = strtok(0, delim); |
| #endif |
| if (!arg) { |
PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", |
PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n", |
configfile, lineno, name, token); |
configfile, lineno, name, token); |
return -1; |
return -1; |
Line 3780 static int ParseToken(char * token, dev_config & cfg)
|
Line 4027 static int ParseToken(char * token, dev_config & cfg)
|
badarg = 1; |
badarg = 1; |
} |
} |
break; |
break; |
|
|
|
case 'e': |
|
// Various ATA settings |
|
if (!(arg = strtok(NULL, delim))) { |
|
missingarg = true; |
|
} |
|
else { |
|
char arg2[16+1]; unsigned val; |
|
int n1 = -1, n2 = -1, n3 = -1, len = strlen(arg); |
|
if (sscanf(arg, "%16[^,=]%n%*[,=]%n%u%n", arg2, &n1, &n2, &val, &n3) >= 1 |
|
&& (n1 == len || n2 > 0)) { |
|
bool on = (n2 > 0 && !strcmp(arg+n2, "on")); |
|
bool off = (n2 > 0 && !strcmp(arg+n2, "off")); |
|
if (n3 != len) |
|
val = ~0U; |
|
|
|
if (!strcmp(arg2, "aam")) { |
|
if (off) |
|
cfg.set_aam = -1; |
|
else if (val <= 254) |
|
cfg.set_aam = val + 1; |
|
else |
|
badarg = true; |
|
} |
|
else if (!strcmp(arg2, "apm")) { |
|
if (off) |
|
cfg.set_apm = -1; |
|
else if (1 <= val && val <= 254) |
|
cfg.set_apm = val + 1; |
|
else |
|
badarg = true; |
|
} |
|
else if (!strcmp(arg2, "lookahead")) { |
|
if (off) |
|
cfg.set_lookahead = -1; |
|
else if (on) |
|
cfg.set_lookahead = 1; |
|
else |
|
badarg = true; |
|
} |
|
else if (!strcmp(arg, "security-freeze")) { |
|
cfg.set_security_freeze = true; |
|
} |
|
else if (!strcmp(arg2, "standby")) { |
|
if (off) |
|
cfg.set_standby = 0 + 1; |
|
else if (val <= 255) |
|
cfg.set_standby = val + 1; |
|
else |
|
badarg = true; |
|
} |
|
else if (!strcmp(arg2, "wcache")) { |
|
if (off) |
|
cfg.set_wcache = -1; |
|
else if (on) |
|
cfg.set_wcache = 1; |
|
else |
|
badarg = true; |
|
} |
|
else |
|
badarg = true; |
|
} |
|
else |
|
badarg = true; |
|
} |
|
break; |
|
|
default: |
default: |
// Directive not recognized |
// Directive not recognized |
PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n", |
PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n", |
Line 3812 static int ParseToken(char * token, dev_config & cfg)
|
Line 4126 static int ParseToken(char * token, dev_config & cfg)
|
// |
// |
// Return values are: |
// Return values are: |
// 1: parsed a normal line |
// 1: parsed a normal line |
// 0: found comment or blank line | // 0: found DEFAULT setting or comment or blank line |
// -1: found SCANDIRECTIVE line |
// -1: found SCANDIRECTIVE line |
// -2: found an error |
// -2: found an error |
// |
// |
// Note: this routine modifies *line from the caller! |
// Note: this routine modifies *line from the caller! |
static int ParseConfigLine(dev_config_vector & conf_entries, int /*entry*/, int lineno, /*const*/ char * line) | static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & default_conf, int lineno, /*const*/ char * line) |
{ |
{ |
char *token=NULL; |
|
char *name=NULL; |
|
const char *delim = " \n\t"; |
const char *delim = " \n\t"; |
int devscan=0; |
|
|
|
// get first token: device name. If a comment, skip line |
// get first token: device name. If a comment, skip line |
if (!(name=strtok(line,delim)) || *name=='#') { | const char * name = strtok(line, delim); |
| if (!name || *name == '#') |
return 0; |
return 0; |
} |
|
|
|
// Have we detected the SCANDIRECTIVE directive? | // Check device name for DEFAULT or DEVICESCAN |
if (!strcmp(SCANDIRECTIVE,name)){ | int retval; |
devscan=1; | if (!strcmp("DEFAULT", name)) { |
| retval = 0; |
| // Restart with empty defaults |
| default_conf = dev_config(); |
} |
} |
| else { |
// We've got a legit entry, make space to store it | retval = (!strcmp(SCANDIRECTIVE, name) ? -1 : 1); |
conf_entries.push_back( dev_config() ); | // Init new entry with current defaults |
dev_config & cfg = conf_entries.back(); | conf_entries.push_back(default_conf); |
| } |
| dev_config & cfg = (retval ? conf_entries.back() : default_conf); |
|
|
cfg.name = name; // Later replaced by dev->get_info().info_name |
cfg.name = name; // Later replaced by dev->get_info().info_name |
cfg.dev_name = name; // If DEVICESCAN later replaced by get->dev_info().dev_name |
cfg.dev_name = name; // If DEVICESCAN later replaced by get->dev_info().dev_name |
|
cfg.lineno = lineno; |
|
|
// Store line number, and by default check for both device types. |
|
cfg.lineno=lineno; |
|
|
|
// parse tokens one at a time from the file. |
// parse tokens one at a time from the file. |
while ((token=strtok(NULL,delim))){ | while (char * token = strtok(0, delim)) { |
int retval=ParseToken(token,cfg); | int rc = ParseToken(token, cfg); |
| if (rc < 0) |
if (retval==0) | |
// No tokens left: | |
break; | |
| |
if (retval>0) { | |
// Parsed token | |
#if (0) | |
PrintOut(LOG_INFO,"Parsed token %s\n",token); | |
#endif | |
continue; | |
} | |
| |
if (retval<0) { | |
// error found on the line |
// error found on the line |
return -2; |
return -2; |
} | |
| if (rc == 0) |
| // No tokens left |
| break; |
| |
| // PrintOut(LOG_INFO,"Parsed token %s\n",token); |
} |
} |
| |
| // Don't perform checks below for DEFAULT entries |
| if (retval == 0) |
| return retval; |
| |
// If NO monitoring directives are set, then set all of them. |
// If NO monitoring directives are set, then set all of them. |
if (!( cfg.smartcheck || cfg.selftest |
if (!( cfg.smartcheck || cfg.selftest |
|| cfg.errorlog || cfg.xerrorlog |
|| cfg.errorlog || cfg.xerrorlog |
Line 3900 static int ParseConfigLine(dev_config_vector & conf_en
|
Line 4210 static int ParseConfigLine(dev_config_vector & conf_en
|
cfg.name.c_str(), cfg.lineno, configfile); |
cfg.name.c_str(), cfg.lineno, configfile); |
return -2; |
return -2; |
} |
} |
// From here on the sign of <nomailer> is address.empty() and !cfg.emailcmdline.empty() | // From here on the sign of <nomailer> is cfg.emailaddress.empty() and !cfg.emailcmdline.empty() |
cfg.emailaddress.clear(); |
cfg.emailaddress.clear(); |
} |
} |
|
|
if (devscan) | return retval; |
return -1; | |
else | |
return 1; | |
} |
} |
|
|
// Parses a configuration file. Return values are: |
// Parses a configuration file. Return values are: |
Line 3942 static int ParseConfigFile(dev_config_vector & conf_en
|
Line 4249 static int ParseConfigFile(dev_config_vector & conf_en
|
else // read from stdin ('-c -' option) |
else // read from stdin ('-c -' option) |
f.open(stdin); |
f.open(stdin); |
|
|
|
// Start with empty defaults |
|
dev_config default_conf; |
|
|
// No configuration file found -- use fake one |
// No configuration file found -- use fake one |
int entry = 0; |
int entry = 0; |
if (!f) { |
if (!f) { |
char fakeconfig[] = SCANDIRECTIVE" -a"; // TODO: Remove this hack, build cfg_entry. |
char fakeconfig[] = SCANDIRECTIVE" -a"; // TODO: Remove this hack, build cfg_entry. |
|
|
if (ParseConfigLine(conf_entries, entry, 0, fakeconfig) != -1) | if (ParseConfigLine(conf_entries, default_conf, 0, fakeconfig) != -1) |
throw std::logic_error("Internal error parsing "SCANDIRECTIVE); |
throw std::logic_error("Internal error parsing "SCANDIRECTIVE); |
return 0; |
return 0; |
} |
} |
Line 3979 static int ParseConfigFile(dev_config_vector & conf_en
|
Line 4289 static int ParseConfigFile(dev_config_vector & conf_en
|
// are we at the end of the file? |
// are we at the end of the file? |
if (!code){ |
if (!code){ |
if (cont) { |
if (cont) { |
scandevice = ParseConfigLine(conf_entries, entry, contlineno, fullline); | scandevice = ParseConfigLine(conf_entries, default_conf, contlineno, fullline); |
// See if we found a SCANDIRECTIVE directive |
// See if we found a SCANDIRECTIVE directive |
if (scandevice==-1) |
if (scandevice==-1) |
return 0; |
return 0; |
Line 4023 static int ParseConfigFile(dev_config_vector & conf_en
|
Line 4333 static int ParseConfigFile(dev_config_vector & conf_en
|
} |
} |
|
|
// copy string so far into fullline, and increment length |
// copy string so far into fullline, and increment length |
strcpy(fullline+cont,line); | snprintf(fullline+cont, sizeof(fullline)-cont, "%s" ,line); |
cont+=len; |
cont+=len; |
|
|
// is this a continuation line. If so, replace \ by space and look at next line |
// is this a continuation line. If so, replace \ by space and look at next line |
Line 4033 static int ParseConfigFile(dev_config_vector & conf_en
|
Line 4343 static int ParseConfigFile(dev_config_vector & conf_en
|
} |
} |
|
|
// Not a continuation line. Parse it |
// Not a continuation line. Parse it |
scandevice = ParseConfigLine(conf_entries, entry, contlineno, fullline); | scandevice = ParseConfigLine(conf_entries, default_conf, contlineno, fullline); |
|
|
// did we find a scandevice directive? |
// did we find a scandevice directive? |
if (scandevice==-1) |
if (scandevice==-1) |
Line 4084 static void check_abs_path(char option, const std::str
|
Line 4394 static void check_abs_path(char option, const std::str
|
// version/license/copyright messages |
// version/license/copyright messages |
static void ParseOpts(int argc, char **argv) |
static void ParseOpts(int argc, char **argv) |
{ |
{ |
// Init default configfile path | // Init default path names |
#ifndef _WIN32 |
#ifndef _WIN32 |
configfile = SMARTMONTOOLS_SYSCONFDIR"/smartd.conf"; |
configfile = SMARTMONTOOLS_SYSCONFDIR"/smartd.conf"; |
|
warning_script = SMARTMONTOOLS_SYSCONFDIR"/smartd_warning.sh"; |
#else |
#else |
static std::string configfile_str = get_exe_dir() + "/smartd.conf"; | std::string exedir = get_exe_dir(); |
| static std::string configfile_str = exedir + "/smartd.conf"; |
configfile = configfile_str.c_str(); |
configfile = configfile_str.c_str(); |
|
warning_script = exedir + "/smartd_warning.cmd"; |
#endif |
#endif |
|
|
// Please update GetValidArgList() if you edit shortopts |
// Please update GetValidArgList() if you edit shortopts |
static const char shortopts[] = "c:l:q:dDni:p:r:s:A:B:Vh?" | static const char shortopts[] = "c:l:q:dDni:p:r:s:A:B:w:Vh?" |
#ifdef HAVE_LIBCAP_NG |
#ifdef HAVE_LIBCAP_NG |
"C" |
"C" |
#endif |
#endif |
Line 4116 static void ParseOpts(int argc, char **argv)
|
Line 4429 static void ParseOpts(int argc, char **argv)
|
{ "savestates", required_argument, 0, 's' }, |
{ "savestates", required_argument, 0, 's' }, |
{ "attributelog", required_argument, 0, 'A' }, |
{ "attributelog", required_argument, 0, 'A' }, |
{ "drivedb", required_argument, 0, 'B' }, |
{ "drivedb", required_argument, 0, 'B' }, |
|
{ "warnexec", required_argument, 0, 'w' }, |
{ "version", no_argument, 0, 'V' }, |
{ "version", no_argument, 0, 'V' }, |
{ "license", no_argument, 0, 'V' }, |
{ "license", no_argument, 0, 'V' }, |
{ "copyright", no_argument, 0, 'V' }, |
{ "copyright", no_argument, 0, 'V' }, |
Line 4277 static void ParseOpts(int argc, char **argv)
|
Line 4591 static void ParseOpts(int argc, char **argv)
|
debugmode = savedebug; |
debugmode = savedebug; |
} |
} |
break; |
break; |
|
case 'w': |
|
warning_script = optarg; |
|
break; |
case 'V': |
case 'V': |
// print version and CVS info |
// print version and CVS info |
debugmode = 1; |
debugmode = 1; |
Line 4478 static int ReadOrMakeConfigEntries(dev_config_vector &
|
Line 4795 static int ReadOrMakeConfigEntries(dev_config_vector &
|
return conf_entries.size(); |
return conf_entries.size(); |
} |
} |
|
|
|
// Return true if TYPE contains a RAID drive number |
|
static bool is_raid_type(const char * type) |
|
{ |
|
if (str_starts_with(type, "sat,")) |
|
return false; |
|
int i; |
|
if (sscanf(type, "%*[^,],%d", &i) != 1) |
|
return false; |
|
return true; |
|
} |
|
|
|
// Return true if DEV is already in DEVICES[0..NUMDEVS) or IGNORED[*] |
|
static bool is_duplicate_device(const smart_device * dev, |
|
const smart_device_list & devices, unsigned numdevs, |
|
const dev_config_vector & ignored) |
|
{ |
|
const smart_device::device_info & info1 = dev->get_info(); |
|
bool is_raid1 = is_raid_type(info1.dev_type.c_str()); |
|
|
|
for (unsigned i = 0; i < numdevs; i++) { |
|
const smart_device::device_info & info2 = devices.at(i)->get_info(); |
|
// -d TYPE options must match if RAID drive number is specified |
|
if ( info1.dev_name == info2.dev_name |
|
&& ( info1.dev_type == info2.dev_type |
|
|| !is_raid1 || !is_raid_type(info2.dev_type.c_str()))) |
|
return true; |
|
} |
|
|
|
for (unsigned i = 0; i < ignored.size(); i++) { |
|
const dev_config & cfg2 = ignored.at(i); |
|
if ( info1.dev_name == cfg2.dev_name |
|
&& ( info1.dev_type == cfg2.dev_type |
|
|| !is_raid1 || !is_raid_type(cfg2.dev_type.c_str()))) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
// This function tries devices from conf_entries. Each one that can be |
// This function tries devices from conf_entries. Each one that can be |
// registered is moved onto the [ata|scsi]devices lists and removed |
// registered is moved onto the [ata|scsi]devices lists and removed |
// from the conf_entries list. |
// from the conf_entries list. |
Line 4491 static void RegisterDevices(const dev_config_vector &
|
Line 4845 static void RegisterDevices(const dev_config_vector &
|
states.clear(); |
states.clear(); |
|
|
// Register entries |
// Register entries |
|
dev_config_vector ignored_entries; |
|
unsigned numnoscan = 0; |
for (unsigned i = 0; i < conf_entries.size(); i++){ |
for (unsigned i = 0; i < conf_entries.size(); i++){ |
|
|
dev_config cfg = conf_entries[i]; |
dev_config cfg = conf_entries[i]; |
|
|
|
if (cfg.ignore) { |
|
// Store for is_duplicate_device() check and ignore |
|
PrintOut(LOG_INFO, "Device: %s%s%s%s, ignored\n", cfg.name.c_str(), |
|
(!cfg.dev_type.empty() ? " [" : ""), |
|
cfg.dev_type.c_str(), |
|
(!cfg.dev_type.empty() ? "]" : "")); |
|
ignored_entries.push_back(cfg); |
|
continue; |
|
} |
|
|
// get device of appropriate type |
// get device of appropriate type |
smart_device_auto_ptr dev; |
smart_device_auto_ptr dev; |
bool scanning = false; |
bool scanning = false; |
Line 4502 static void RegisterDevices(const dev_config_vector &
|
Line 4868 static void RegisterDevices(const dev_config_vector &
|
// Device may already be detected during devicescan |
// Device may already be detected during devicescan |
if (i < scanned_devs.size()) { |
if (i < scanned_devs.size()) { |
dev = scanned_devs.release(i); |
dev = scanned_devs.release(i); |
if (dev) | if (dev) { |
| // Check for a preceding non-DEVICESCAN entry for the same device |
| if ( (numnoscan || !ignored_entries.empty()) |
| && is_duplicate_device(dev.get(), devices, numnoscan, ignored_entries)) { |
| PrintOut(LOG_INFO, "Device: %s, duplicate, ignored\n", dev->get_info_name()); |
| continue; |
| } |
scanning = true; |
scanning = true; |
|
} |
} |
} |
|
|
if (!dev) { |
if (!dev) { |
Line 4568 static void RegisterDevices(const dev_config_vector &
|
Line 4941 static void RegisterDevices(const dev_config_vector &
|
configs.push_back(cfg); |
configs.push_back(cfg); |
states.push_back(state); |
states.push_back(state); |
devices.push_back(dev); |
devices.push_back(dev); |
|
if (!scanning) |
|
numnoscan = devices.size(); |
} |
} |
// if device is explictly listed and we can't register it, then |
// if device is explictly listed and we can't register it, then |
// exit unless the user has specified that the device is removable |
// exit unless the user has specified that the device is removable |
Line 4580 static void RegisterDevices(const dev_config_vector &
|
Line 4955 static void RegisterDevices(const dev_config_vector &
|
} |
} |
} |
} |
} |
} |
|
|
|
init_disable_standby_check(configs); |
} |
} |
|
|
|
|
Line 4645 static int main_worker(int argc, char **argv)
|
Line 5022 static int main_worker(int argc, char **argv)
|
// Should we (re)read the config file? |
// Should we (re)read the config file? |
if (firstpass || caughtsigHUP){ |
if (firstpass || caughtsigHUP){ |
if (!firstpass) { |
if (!firstpass) { |
#ifdef __CYGWIN__ |
|
// Workaround for missing SIGQUIT via keyboard on Cygwin |
|
if (caughtsigHUP==2) { |
|
// Simulate SIGQUIT if another SIGINT arrives soon |
|
caughtsigHUP=0; |
|
sleep(1); |
|
if (caughtsigHUP==2) { |
|
caughtsigEXIT=SIGQUIT; |
|
continue; |
|
} |
|
caughtsigHUP=2; |
|
} |
|
#endif |
|
// Write state files |
// Write state files |
if (!state_path_prefix.empty()) |
if (!state_path_prefix.empty()) |
write_all_dev_states(configs, states); |
write_all_dev_states(configs, states); |