version 1.1.1.2, 2012/10/09 09:36:45
|
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-12 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 115 typedef int pid_t;
|
Line 107 typedef int pid_t;
|
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 168 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 184 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 248 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 264 struct dev_config
|
Line 257 struct dev_config
|
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 282 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_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_apm; // disable(-1), enable(2..255->1..254) Advanced Power Management |
int set_lookahead; // disable(-1), enable(1) read look-ahead |
int set_lookahead; // disable(-1), enable(1) read look-ahead |
Line 307 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 319 dev_config::dev_config()
|
Line 314 dev_config::dev_config()
|
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 330 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_aam(0), set_apm(0), |
set_lookahead(0), |
set_lookahead(0), |
set_standby(0), |
set_standby(0), |
Line 388 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 423 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 |
Line 450 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), |
Line 721 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 729 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 849 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 |
Line 908 static void MailWarning(const dev_config & cfg, dev_st
|
Line 941 static void MailWarning(const dev_config & cfg, dev_st
|
|
|
// 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 932 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 964 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 990 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 1182 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 1321 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 1335 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 1347 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 1506 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" |
Line 1534 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 1562 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 1626 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 1658 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 1670 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 1682 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) |
Line 1845 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 1854 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 1861 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 1873 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 1997 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; |
} |
} |
|
|
Line 2044 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 2064 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 2084 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 2094 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 2235 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 2249 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; |
if (len < 36) { |
if (len < 36) { |
Line 2266 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
Line 2195 static int SCSIDeviceScan(dev_config & cfg, dev_state
|
"skip\n", device, pdt); |
"skip\n", device, pdt); |
return 2; |
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 2343 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 2361 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 2403 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 |
|
if (!state_path_prefix.empty()) { |
|
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); |
|
} |
|
|
|
// Make sure that init_standby_check() ignores SCSI devices |
// Make sure that init_standby_check() ignores SCSI devices |
cfg.offlinests_ns = cfg.selfteststs_ns = false; |
cfg.offlinests_ns = cfg.selfteststs_ns = false; |
|
|
// 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 2461 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 2755 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 2834 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 2908 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 2937 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 3004 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 3195 static int ATACheckDevice(const dev_config & cfg, dev_
|
Line 3166 static int ATACheckDevice(const dev_config & cfg, dev_
|
|
|
// 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 3251 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 3266 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 3283 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 3290 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 3302 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; |
} |
} |
Line 3561 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; |
case 'e': |
case 'e': |
PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], " |
PrintOut(priority, "aam,[N|off], apm,[N|off], lookahead,[on|off], " |
"security-freeze, standby,[N|off], wcache,[on|off]"); |
"security-freeze, standby,[N|off], wcache,[on|off]"); |
Line 3627 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 3696 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 3706 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 3894 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 3911 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 4166 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(); |
} |
} |
|
|
Line 4289 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 4350 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 4382 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 4543 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 4744 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 4757 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 4768 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 4834 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 |