From 854a4fca71d174c8320a50174ea51e8cc3c3a292 Mon Sep 17 00:00:00 2001 From: Imanol-Mikel Barba Sabariego Date: Wed, 26 Jun 2019 18:20:20 +0200 Subject: [PATCH] Improved check_smart to account for SSDs and made overall code structure better --- check_smart/auxiliar.cpp | 2 +- check_smart/auxiliar.h | 2 +- check_smart/check_smart.cpp | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- check_smart/check_smart.h | 45 ++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 300 insertions(+), 190 deletions(-) diff --git a/check_smart/auxiliar.cpp b/check_smart/auxiliar.cpp index 9599ce0..abea7dc 100755 --- a/check_smart/auxiliar.cpp +++ b/check_smart/auxiliar.cpp @@ -52,4 +52,4 @@ int exec(string cmd, string *output) } } return pclose(pipe)/256; -} \ No newline at end of file +} diff --git a/check_smart/auxiliar.h b/check_smart/auxiliar.h index 74adba2..b68c3de 100755 --- a/check_smart/auxiliar.h +++ b/check_smart/auxiliar.h @@ -15,7 +15,7 @@ using namespace std; -extern char *servicename; +extern const char *servicename; int str2int(string str); string int2str(int x); diff --git a/check_smart/check_smart.cpp b/check_smart/check_smart.cpp index 0945582..ad10af5 100644 --- a/check_smart/check_smart.cpp +++ b/check_smart/check_smart.cpp @@ -1,200 +1,275 @@ #include "check_smart.h" -char *servicename = (char*)"SMART"; +const char *servicename = (const char*)"SMART"; -int evalStatus(char* disk, string reallocatedCount, string currentPending, string offlineUncorrectable, string *status) +int getSmartAttrValue(string line) { - int returnStatus = 0; - *status = string(disk) + " reallocated_count:"; - string reallocatedNum = reallocatedCount.substr(reallocatedCount.find_last_of(" ")+1); - if(reallocatedNum != "0") - { - returnStatus = 1; - } - *status += reallocatedNum + " current_pending:"; - string currentNum = currentPending.substr(currentPending.find_last_of(" ")+1); - if(currentNum != "0") - { - returnStatus = 2; - } - *status += currentNum + " offline_uncorrectable:"; - string offlineNum = offlineUncorrectable.substr(offlineUncorrectable.find_last_of(" ")+1); - if(offlineNum != "0") - { - returnStatus = 2; - } - *status += offlineNum; - return returnStatus; + return stoi(line.substr(line.find_last_of(" ")+1)); +} + +int getSmartAttrID(string line) +{ + size_t first = line.find_first_not_of(' '); + size_t last = line.find_first_of(' ',first); + int id = -1; + try {id = stoi(line.substr(first,last-first));} + catch(...){} + return id; +} + +int checkDriveType(const char* disk) +{ + string output = ""; + string RRLine = ""; + string line= ""; + + int rc = run_smartctl_cmd(string(SMARTCTL_CMD_INFO), disk,&output); + if(rc) + { + cout << "Error reading SMART data from disk " << disk << endl; + exit(3); + } + + stringstream sstream; + sstream.str(output); + while(getline(sstream,line) && RRLine == "") + { + size_t found = line.find(ROTATION_INFO_STR); + if(found != string::npos) {RRLine = line;} + } + if(RRLine != "") + { + size_t found = RRLine.find(SSD_DEV_STR); + if(found != string::npos) {return SSD;} + } + return HDD; +} + +map prepareAttrMap(int driveType) +{ + map map; + + SMARTAttr realloc; + realloc.name = "Reallocated_Sector_Ct"; + realloc.value = -1; + realloc.severity = WARN; + map[REALLOC_SEC_COUNT_ID] = realloc; + + SMARTAttr pending; + SMARTAttr off_uncorrect; + SMARTAttr wear; + SMARTAttr badblocks; + SMARTAttr rep_uncorrect; + + switch(driveType) + { + case HDD: + pending.name = "Current_Pending_Sector"; + pending.value = -1; + pending.severity = CRIT; + map[CURRENT_PENDING_SEC_ID] = pending; + + off_uncorrect.name = "Offline_Uncorrectable"; + off_uncorrect.value = -1; + off_uncorrect.severity = CRIT; + map[OFFLINE_UNCORRECT_ID] = off_uncorrect; + + break; + case SSD: + wear.name = "Wear_Leveling_Count"; + wear.value = -1; + wear.severity = WARN; + map[WEAR_COUNT_ID] = wear; + + badblocks.name = "Runtime_Bad_Block"; + badblocks.value = -1; + badblocks.severity = CRIT; + map[RUNTIME_BAD_BLOCKS_IDD] = badblocks; + + rep_uncorrect.name = "Reported_Uncorrect"; + rep_uncorrect.value = -1; + rep_uncorrect.severity = CRIT; + map[REP_UNCORRECT_ID] = rep_uncorrect; + + break; + } + return map; +} + +int evalStatus(const char* disk, int driveType, string *status) +{ + string output = ""; + string line = ""; + + int rc = run_smartctl_cmd(string(SMARTCTL_CMD_ATTRS), disk,&output); + if(rc) + { + cout << "Error reading SMART data from disk " << disk << endl; + exit(UNKN); + } + + map attrMap = prepareAttrMap(driveType); + + stringstream sstream; + sstream.str(output); + while(getline(sstream,line)) + { + for(map::iterator it = attrMap.begin(); it != attrMap.end(); ++it) + { + int id = it->first; + if(getSmartAttrID(line) == id) {attrMap[id].value = getSmartAttrValue(line);} + } + } + int ret = OK; + *status = string(disk); + for(map::iterator it = attrMap.begin(); it != attrMap.end(); ++it) + { + int id = it->first; + SMARTAttr attr = it->second; + + if(attr.value == -1) + { + *status = string(disk) + " status UNKNOWN"; + return UNKN; + } + int veredict = 0; + if(attr.value) {veredict = attr.severity;} + switch(veredict) + { + case OK: + break; + case WARN: + if(ret == OK) {ret = WARN;} + break; + case CRIT: + ret = CRIT; + break; + } + *status += " " + attr.name + ":" + to_string(attr.value); + } + return ret; +} + +int run_smartctl_cmd(const string command, const char* disk, string* output) +{ + int uid = getuid(); + setreuid(0,0); + + int rc = exec(command + disk,output); + + setreuid(uid,0); + return rc; } void printVersion() { - cout << "check_smart v" << VERSION << endl << endl; + cout << "check_smart v" << VERSION << endl << endl; } void printHelp(bool longVersion) { - if(longVersion) - { - printVersion(); - cout << "Checks for pending, reallocated or uncorrectable sectors in disks using SMART" << endl << "WARNING: Requires the setuid bit to be set to run as root" << endl << endl; - printHelp(false); - cout << "Options:" << endl; - cout << " -h" << endl; - cout << " Print detailed help screen" << endl; - cout << " -V" << endl; - cout << " Print version information" << endl; - cout << " DISKS" << endl; - cout << " Disks for which to retrieve S.M.A.R.T data" << endl << endl; - return; - } - cout << "Usage: " << endl << "check_smart [-hV] DISKS..." << endl << endl; + if(longVersion) + { + printVersion(); + cout << "Checks for pending, reallocated or uncorrectable sectors in disks using SMART" << endl << "WARNING: Requires the setuid bit to be set to run as root" << endl << endl; + printHelp(false); + cout << "Options:" << endl; + cout << " -h" << endl; + cout << " Print detailed help screen" << endl; + cout << " -V" << endl; + cout << " Print version information" << endl; + cout << " DISKS" << endl; + cout << " Disks for which to retrieve S.M.A.R.T data" << endl << endl; + return; + } + cout << "Usage: " << endl << "check_smart [-hV] DISKS..." << endl << endl; +} + +void set_timeout(unsigned int sec) +{ + struct itimerval timer; + timer.it_value.tv_sec = sec; + timer.it_value.tv_usec = 0; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 0; + setitimer (ITIMER_VIRTUAL, &timer, 0); + + struct sigaction sa; + memset (&sa, 0, sizeof (sa)); + sa.sa_handler = &timer_handler; + sigaction (SIGVTALRM, &sa, 0); +<<<<<<< HEAD +======= + +>>>>>>> f3a48aed56edc3f5053f68421dbc764d5930797f } int main(int argc, char **argv) { - struct itimerval timer; - timer.it_value.tv_sec = 10; - timer.it_value.tv_usec = 0; - timer.it_interval.tv_sec = 0; - timer.it_interval.tv_usec = 0; - setitimer (ITIMER_VIRTUAL, &timer, 0); - - struct sigaction sa; - memset (&sa, 0, sizeof (sa)); - sa.sa_handler = &timer_handler; - sigaction (SIGVTALRM, &sa, 0); - - int c; - while ((c = getopt (argc, argv, "Vh")) != -1) - { - switch(c) - { - case 'h': - printHelp(true); - return 0; - case 'V': - printVersion(); - return 0; - case '?': - printHelp(false); - return 3; - } - } - - if(argc == 1) - { - cout << "No disks checked" << endl; - return 3; - } - - string *results = new string[argc-1]; - int returnCode = 0; - - string command = string(SMARTCTL_CMD); - stringstream sstream; - string line = ""; - string reallocatedCount = ""; - string currentPending = ""; - string offlineUncorrectable = ""; - string status = ""; - for(int i = 1; i < argc; i++) - { - string output; - int uid = getuid(); - int gid = getgid(); - setuid(0); - setgid(0); - int rc = exec(command + argv[i],&output); - setuid(uid); - setgid(gid); - if(rc) - { - cout << "Error reading SMART data from disk" << endl; - exit(3); - } - sstream.str(output); - while(getline(sstream,line)) - { - int j = 0; - while(line[j] == ' ') - { - j++; - } - size_t spacePos = line.find_first_of(" ",j); - if(line.substr(j,spacePos-j) == "5") - { - reallocatedCount = line; - } - else if(line.substr(j,spacePos-j) == "197") - { - currentPending = line; - } - else if(line.substr(j,spacePos-j) == "198") - { - offlineUncorrectable = line; - } - } - if((reallocatedCount == "") || (currentPending == "") || (offlineUncorrectable == "")) - { - returnCode = 3; - status = string(argv[i]) + " status UNKNOWN"; - } - else - { - switch(evalStatus(argv[i],reallocatedCount,currentPending,offlineUncorrectable,&status)) - { - case 0: - //everything is OK - break; - case 1: - //Warning issued - if(!returnCode) - { - returnCode = 1; - } - break; - case 2: - //Critical issued - returnCode = 2; - break; - } - } - results[i-1] = status; - reallocatedCount = ""; - currentPending = ""; - offlineUncorrectable = ""; - output = ""; - line = ""; - status = ""; - sstream.str(""); - sstream.clear(); - } - cout << servicename; - if(returnCode == 0) - { - cout << " OK - disk status: "; - } - else if(returnCode == 1) - { - cout << " WARNING - disk status: "; - } - else if(returnCode == 2) - { - cout << " CRITICAL - disk status: "; - } - else if(returnCode == 3) - { - cout << " UNKNOWN - disk status: "; - } - for(int i = 0; i < (argc-1); i++) - { - cout << results[i]; - if(i != (argc-2)) - { - cout << ", "; - } - } - cout << endl; - return returnCode; + set_timeout(10); + int c; + while ((c = getopt (argc, argv, "Vh")) != -1) + { + switch(c) + { + case 'h': + printHelp(true); + return OK; + case 'V': + printVersion(); + return OK; + case '?': + printHelp(false); + return UNKN; + } + } + + if(argc == 1) + { + cout << "No disks checked" << endl; + return 3; + } + + string *results = new string[argc-1]; + int returnCode = OK; + + for(int i = 1; i < argc; i++) + { + int driveType = checkDriveType(argv[i]); + switch(driveType) + { + case HDD: + case SSD: + break; + default: + cout << "Unknown disk type" << endl; + return UNKN; + break; + } + + switch(evalStatus(argv[i],driveType,&(results[i-1]))) + { + case OK: + break; + case WARN: + if(returnCode == OK) {returnCode = WARN;} + break; + case CRIT: + returnCode = CRIT; + break; + } + } + + cout << servicename; + if(returnCode == OK) {cout << " OK - disk status: ";} + else if(returnCode == WARN) {cout << " WARNING - disk status: ";} + else if(returnCode == CRIT) {cout << " CRITICAL - disk status: ";} + else if(returnCode == UNKN) {cout << " UNKNOWN - disk status: ";} + for(int i = 0; i < (argc-1); i++) + { + cout << results[i]; + if(i != (argc-2)) {cout << ", ";} + } + cout << endl; + return returnCode; } diff --git a/check_smart/check_smart.h b/check_smart/check_smart.h index cbf2f6b..877cce3 100755 --- a/check_smart/check_smart.h +++ b/check_smart/check_smart.h @@ -3,23 +3,58 @@ #include #include +#include #include -#include -#include +#include +#include #include #include #include "auxiliar.h" -#define SMARTCTL_CMD "/usr/sbin/smartctl -A " -#define VERSION "1.0" +#define OK 0 +#define WARN 1 +#define CRIT 2 +#define UNKN 3 + +#define VERSION "1.2" + +#define SMARTCTL_CMD_ATTRS "/usr/sbin/smartctl -A " +#define SMARTCTL_CMD_INFO "/usr/sbin/smartctl -i " + +#define ROTATION_INFO_STR "Rotation Rate:" +#define SSD_DEV_STR "Solid State Device" + +#define REALLOC_SEC_COUNT_ID 5 +#define CURRENT_PENDING_SEC_ID 197 +#define OFFLINE_UNCORRECT_ID 198 +#define WEAR_COUNT_ID 177 +#define RUNTIME_BAD_BLOCKS_IDD 183 +#define REP_UNCORRECT_ID 187 + +#define HDD 0 +#define SSD 1 using namespace std; -int evalStatus(char* disk, string reallocatedCount, string currentPending, string offlineUncorrectable, string *status); +struct SMARTAttr +{ + string name; + int value; + int severity; +}; typedef struct SMARTAttr SMARTAttr; + + +map prepareAttrMap(int driveType); +int getSmartAttrValue(string line); +int getSmartAttrID(string line); +int run_smartctl_cmd(const string command, const char* disk, string* output); +int checkDriveType(const char* disk); +int evalStatus(const char* disk, int driveType, string* status); void printVersion(); void printHelp(bool longVersion); +void set_timeout(unsigned int sec); #endif -- libgit2 0.22.2