Commit 854a4fca71d174c8320a50174ea51e8cc3c3a292

Authored by Imanol-Mikel Barba Sabariego
1 parent 1272a895

Improved check_smart to account for SSDs and made overall code structure better

check_smart/auxiliar.cpp
@@ -52,4 +52,4 @@ int exec(string cmd, string *output) @@ -52,4 +52,4 @@ int exec(string cmd, string *output)
52 } 52 }
53 } 53 }
54 return pclose(pipe)/256; 54 return pclose(pipe)/256;
55 -}  
56 \ No newline at end of file 55 \ No newline at end of file
  56 +}
check_smart/auxiliar.h
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 15
16 using namespace std; 16 using namespace std;
17 17
18 -extern char *servicename; 18 +extern const char *servicename;
19 19
20 int str2int(string str); 20 int str2int(string str);
21 string int2str(int x); 21 string int2str(int x);
check_smart/check_smart.cpp
1 #include "check_smart.h" 1 #include "check_smart.h"
2 2
3 -char *servicename = (char*)"SMART"; 3 +const char *servicename = (const char*)"SMART";
4 4
5 -int evalStatus(char* disk, string reallocatedCount, string currentPending, string offlineUncorrectable, string *status) 5 +int getSmartAttrValue(string line)
6 { 6 {
7 - int returnStatus = 0;  
8 - *status = string(disk) + " reallocated_count:";  
9 - string reallocatedNum = reallocatedCount.substr(reallocatedCount.find_last_of(" ")+1);  
10 - if(reallocatedNum != "0")  
11 - {  
12 - returnStatus = 1;  
13 - }  
14 - *status += reallocatedNum + " current_pending:";  
15 - string currentNum = currentPending.substr(currentPending.find_last_of(" ")+1);  
16 - if(currentNum != "0")  
17 - {  
18 - returnStatus = 2;  
19 - }  
20 - *status += currentNum + " offline_uncorrectable:";  
21 - string offlineNum = offlineUncorrectable.substr(offlineUncorrectable.find_last_of(" ")+1);  
22 - if(offlineNum != "0")  
23 - {  
24 - returnStatus = 2;  
25 - }  
26 - *status += offlineNum;  
27 - return returnStatus; 7 + return stoi(line.substr(line.find_last_of(" ")+1));
  8 +}
  9 +
  10 +int getSmartAttrID(string line)
  11 +{
  12 + size_t first = line.find_first_not_of(' ');
  13 + size_t last = line.find_first_of(' ',first);
  14 + int id = -1;
  15 + try {id = stoi(line.substr(first,last-first));}
  16 + catch(...){}
  17 + return id;
  18 +}
  19 +
  20 +int checkDriveType(const char* disk)
  21 +{
  22 + string output = "";
  23 + string RRLine = "";
  24 + string line= "";
  25 +
  26 + int rc = run_smartctl_cmd(string(SMARTCTL_CMD_INFO), disk,&output);
  27 + if(rc)
  28 + {
  29 + cout << "Error reading SMART data from disk " << disk << endl;
  30 + exit(3);
  31 + }
  32 +
  33 + stringstream sstream;
  34 + sstream.str(output);
  35 + while(getline(sstream,line) && RRLine == "")
  36 + {
  37 + size_t found = line.find(ROTATION_INFO_STR);
  38 + if(found != string::npos) {RRLine = line;}
  39 + }
  40 + if(RRLine != "")
  41 + {
  42 + size_t found = RRLine.find(SSD_DEV_STR);
  43 + if(found != string::npos) {return SSD;}
  44 + }
  45 + return HDD;
  46 +}
  47 +
  48 +map<int,SMARTAttr> prepareAttrMap(int driveType)
  49 +{
  50 + map<int,SMARTAttr> map;
  51 +
  52 + SMARTAttr realloc;
  53 + realloc.name = "Reallocated_Sector_Ct";
  54 + realloc.value = -1;
  55 + realloc.severity = WARN;
  56 + map[REALLOC_SEC_COUNT_ID] = realloc;
  57 +
  58 + SMARTAttr pending;
  59 + SMARTAttr off_uncorrect;
  60 + SMARTAttr wear;
  61 + SMARTAttr badblocks;
  62 + SMARTAttr rep_uncorrect;
  63 +
  64 + switch(driveType)
  65 + {
  66 + case HDD:
  67 + pending.name = "Current_Pending_Sector";
  68 + pending.value = -1;
  69 + pending.severity = CRIT;
  70 + map[CURRENT_PENDING_SEC_ID] = pending;
  71 +
  72 + off_uncorrect.name = "Offline_Uncorrectable";
  73 + off_uncorrect.value = -1;
  74 + off_uncorrect.severity = CRIT;
  75 + map[OFFLINE_UNCORRECT_ID] = off_uncorrect;
  76 +
  77 + break;
  78 + case SSD:
  79 + wear.name = "Wear_Leveling_Count";
  80 + wear.value = -1;
  81 + wear.severity = WARN;
  82 + map[WEAR_COUNT_ID] = wear;
  83 +
  84 + badblocks.name = "Runtime_Bad_Block";
  85 + badblocks.value = -1;
  86 + badblocks.severity = CRIT;
  87 + map[RUNTIME_BAD_BLOCKS_IDD] = badblocks;
  88 +
  89 + rep_uncorrect.name = "Reported_Uncorrect";
  90 + rep_uncorrect.value = -1;
  91 + rep_uncorrect.severity = CRIT;
  92 + map[REP_UNCORRECT_ID] = rep_uncorrect;
  93 +
  94 + break;
  95 + }
  96 + return map;
  97 +}
  98 +
  99 +int evalStatus(const char* disk, int driveType, string *status)
  100 +{
  101 + string output = "";
  102 + string line = "";
  103 +
  104 + int rc = run_smartctl_cmd(string(SMARTCTL_CMD_ATTRS), disk,&output);
  105 + if(rc)
  106 + {
  107 + cout << "Error reading SMART data from disk " << disk << endl;
  108 + exit(UNKN);
  109 + }
  110 +
  111 + map<int,SMARTAttr> attrMap = prepareAttrMap(driveType);
  112 +
  113 + stringstream sstream;
  114 + sstream.str(output);
  115 + while(getline(sstream,line))
  116 + {
  117 + for(map<int,SMARTAttr>::iterator it = attrMap.begin(); it != attrMap.end(); ++it)
  118 + {
  119 + int id = it->first;
  120 + if(getSmartAttrID(line) == id) {attrMap[id].value = getSmartAttrValue(line);}
  121 + }
  122 + }
  123 + int ret = OK;
  124 + *status = string(disk);
  125 + for(map<int,SMARTAttr>::iterator it = attrMap.begin(); it != attrMap.end(); ++it)
  126 + {
  127 + int id = it->first;
  128 + SMARTAttr attr = it->second;
  129 +
  130 + if(attr.value == -1)
  131 + {
  132 + *status = string(disk) + " status UNKNOWN";
  133 + return UNKN;
  134 + }
  135 + int veredict = 0;
  136 + if(attr.value) {veredict = attr.severity;}
  137 + switch(veredict)
  138 + {
  139 + case OK:
  140 + break;
  141 + case WARN:
  142 + if(ret == OK) {ret = WARN;}
  143 + break;
  144 + case CRIT:
  145 + ret = CRIT;
  146 + break;
  147 + }
  148 + *status += " " + attr.name + ":" + to_string(attr.value);
  149 + }
  150 + return ret;
  151 +}
  152 +
  153 +int run_smartctl_cmd(const string command, const char* disk, string* output)
  154 +{
  155 + int uid = getuid();
  156 + setreuid(0,0);
  157 +
  158 + int rc = exec(command + disk,output);
  159 +
  160 + setreuid(uid,0);
  161 + return rc;
28 } 162 }
29 163
30 void printVersion() 164 void printVersion()
31 { 165 {
32 - cout << "check_smart v" << VERSION << endl << endl; 166 + cout << "check_smart v" << VERSION << endl << endl;
33 } 167 }
34 168
35 void printHelp(bool longVersion) 169 void printHelp(bool longVersion)
36 { 170 {
37 - if(longVersion)  
38 - {  
39 - printVersion();  
40 - 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;  
41 - printHelp(false);  
42 - cout << "Options:" << endl;  
43 - cout << " -h" << endl;  
44 - cout << " Print detailed help screen" << endl;  
45 - cout << " -V" << endl;  
46 - cout << " Print version information" << endl;  
47 - cout << " DISKS" << endl;  
48 - cout << " Disks for which to retrieve S.M.A.R.T data" << endl << endl;  
49 - return;  
50 - }  
51 - cout << "Usage: " << endl << "check_smart [-hV] DISKS..." << endl << endl; 171 + if(longVersion)
  172 + {
  173 + printVersion();
  174 + 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;
  175 + printHelp(false);
  176 + cout << "Options:" << endl;
  177 + cout << " -h" << endl;
  178 + cout << " Print detailed help screen" << endl;
  179 + cout << " -V" << endl;
  180 + cout << " Print version information" << endl;
  181 + cout << " DISKS" << endl;
  182 + cout << " Disks for which to retrieve S.M.A.R.T data" << endl << endl;
  183 + return;
  184 + }
  185 + cout << "Usage: " << endl << "check_smart [-hV] DISKS..." << endl << endl;
  186 +}
  187 +
  188 +void set_timeout(unsigned int sec)
  189 +{
  190 + struct itimerval timer;
  191 + timer.it_value.tv_sec = sec;
  192 + timer.it_value.tv_usec = 0;
  193 + timer.it_interval.tv_sec = 0;
  194 + timer.it_interval.tv_usec = 0;
  195 + setitimer (ITIMER_VIRTUAL, &timer, 0);
  196 +
  197 + struct sigaction sa;
  198 + memset (&sa, 0, sizeof (sa));
  199 + sa.sa_handler = &timer_handler;
  200 + sigaction (SIGVTALRM, &sa, 0);
  201 +<<<<<<< HEAD
  202 +=======
  203 +
  204 +>>>>>>> f3a48aed56edc3f5053f68421dbc764d5930797f
52 } 205 }
53 206
54 int main(int argc, char **argv) 207 int main(int argc, char **argv)
55 { 208 {
56 - struct itimerval timer;  
57 - timer.it_value.tv_sec = 10;  
58 - timer.it_value.tv_usec = 0;  
59 - timer.it_interval.tv_sec = 0;  
60 - timer.it_interval.tv_usec = 0;  
61 - setitimer (ITIMER_VIRTUAL, &timer, 0);  
62 -  
63 - struct sigaction sa;  
64 - memset (&sa, 0, sizeof (sa));  
65 - sa.sa_handler = &timer_handler;  
66 - sigaction (SIGVTALRM, &sa, 0);  
67 -  
68 - int c;  
69 - while ((c = getopt (argc, argv, "Vh")) != -1)  
70 - {  
71 - switch(c)  
72 - {  
73 - case 'h':  
74 - printHelp(true);  
75 - return 0;  
76 - case 'V':  
77 - printVersion();  
78 - return 0;  
79 - case '?':  
80 - printHelp(false);  
81 - return 3;  
82 - }  
83 - }  
84 -  
85 - if(argc == 1)  
86 - {  
87 - cout << "No disks checked" << endl;  
88 - return 3;  
89 - }  
90 -  
91 - string *results = new string[argc-1];  
92 - int returnCode = 0;  
93 -  
94 - string command = string(SMARTCTL_CMD);  
95 - stringstream sstream;  
96 - string line = "";  
97 - string reallocatedCount = "";  
98 - string currentPending = "";  
99 - string offlineUncorrectable = "";  
100 - string status = "";  
101 - for(int i = 1; i < argc; i++)  
102 - {  
103 - string output;  
104 - int uid = getuid();  
105 - int gid = getgid();  
106 - setuid(0);  
107 - setgid(0);  
108 - int rc = exec(command + argv[i],&output);  
109 - setuid(uid);  
110 - setgid(gid);  
111 - if(rc)  
112 - {  
113 - cout << "Error reading SMART data from disk" << endl;  
114 - exit(3);  
115 - }  
116 - sstream.str(output);  
117 - while(getline(sstream,line))  
118 - {  
119 - int j = 0;  
120 - while(line[j] == ' ')  
121 - {  
122 - j++;  
123 - }  
124 - size_t spacePos = line.find_first_of(" ",j);  
125 - if(line.substr(j,spacePos-j) == "5")  
126 - {  
127 - reallocatedCount = line;  
128 - }  
129 - else if(line.substr(j,spacePos-j) == "197")  
130 - {  
131 - currentPending = line;  
132 - }  
133 - else if(line.substr(j,spacePos-j) == "198")  
134 - {  
135 - offlineUncorrectable = line;  
136 - }  
137 - }  
138 - if((reallocatedCount == "") || (currentPending == "") || (offlineUncorrectable == ""))  
139 - {  
140 - returnCode = 3;  
141 - status = string(argv[i]) + " status UNKNOWN";  
142 - }  
143 - else  
144 - {  
145 - switch(evalStatus(argv[i],reallocatedCount,currentPending,offlineUncorrectable,&status))  
146 - {  
147 - case 0:  
148 - //everything is OK  
149 - break;  
150 - case 1:  
151 - //Warning issued  
152 - if(!returnCode)  
153 - {  
154 - returnCode = 1;  
155 - }  
156 - break;  
157 - case 2:  
158 - //Critical issued  
159 - returnCode = 2;  
160 - break;  
161 - }  
162 - }  
163 - results[i-1] = status;  
164 - reallocatedCount = "";  
165 - currentPending = "";  
166 - offlineUncorrectable = "";  
167 - output = "";  
168 - line = "";  
169 - status = "";  
170 - sstream.str("");  
171 - sstream.clear();  
172 - }  
173 - cout << servicename;  
174 - if(returnCode == 0)  
175 - {  
176 - cout << " OK - disk status: ";  
177 - }  
178 - else if(returnCode == 1)  
179 - {  
180 - cout << " WARNING - disk status: ";  
181 - }  
182 - else if(returnCode == 2)  
183 - {  
184 - cout << " CRITICAL - disk status: ";  
185 - }  
186 - else if(returnCode == 3)  
187 - {  
188 - cout << " UNKNOWN - disk status: ";  
189 - }  
190 - for(int i = 0; i < (argc-1); i++)  
191 - {  
192 - cout << results[i];  
193 - if(i != (argc-2))  
194 - {  
195 - cout << ", ";  
196 - }  
197 - }  
198 - cout << endl;  
199 - return returnCode; 209 + set_timeout(10);
  210 + int c;
  211 + while ((c = getopt (argc, argv, "Vh")) != -1)
  212 + {
  213 + switch(c)
  214 + {
  215 + case 'h':
  216 + printHelp(true);
  217 + return OK;
  218 + case 'V':
  219 + printVersion();
  220 + return OK;
  221 + case '?':
  222 + printHelp(false);
  223 + return UNKN;
  224 + }
  225 + }
  226 +
  227 + if(argc == 1)
  228 + {
  229 + cout << "No disks checked" << endl;
  230 + return 3;
  231 + }
  232 +
  233 + string *results = new string[argc-1];
  234 + int returnCode = OK;
  235 +
  236 + for(int i = 1; i < argc; i++)
  237 + {
  238 + int driveType = checkDriveType(argv[i]);
  239 + switch(driveType)
  240 + {
  241 + case HDD:
  242 + case SSD:
  243 + break;
  244 + default:
  245 + cout << "Unknown disk type" << endl;
  246 + return UNKN;
  247 + break;
  248 + }
  249 +
  250 + switch(evalStatus(argv[i],driveType,&(results[i-1])))
  251 + {
  252 + case OK:
  253 + break;
  254 + case WARN:
  255 + if(returnCode == OK) {returnCode = WARN;}
  256 + break;
  257 + case CRIT:
  258 + returnCode = CRIT;
  259 + break;
  260 + }
  261 + }
  262 +
  263 + cout << servicename;
  264 + if(returnCode == OK) {cout << " OK - disk status: ";}
  265 + else if(returnCode == WARN) {cout << " WARNING - disk status: ";}
  266 + else if(returnCode == CRIT) {cout << " CRITICAL - disk status: ";}
  267 + else if(returnCode == UNKN) {cout << " UNKNOWN - disk status: ";}
  268 + for(int i = 0; i < (argc-1); i++)
  269 + {
  270 + cout << results[i];
  271 + if(i != (argc-2)) {cout << ", ";}
  272 + }
  273 + cout << endl;
  274 + return returnCode;
200 } 275 }
check_smart/check_smart.h
@@ -3,23 +3,58 @@ @@ -3,23 +3,58 @@
3 3
4 #include <iostream> 4 #include <iostream>
5 #include <sstream> 5 #include <sstream>
  6 +#include <map>
6 7
7 #include <stdio.h> 8 #include <stdio.h>
8 -#include <cstring>  
9 -#include <cstdlib> 9 +#include <string.h>
  10 +#include <stdlib.h>
10 #include <unistd.h> 11 #include <unistd.h>
11 12
12 #include <sys/time.h> 13 #include <sys/time.h>
13 14
14 #include "auxiliar.h" 15 #include "auxiliar.h"
15 16
16 -#define SMARTCTL_CMD "/usr/sbin/smartctl -A "  
17 -#define VERSION "1.0" 17 +#define OK 0
  18 +#define WARN 1
  19 +#define CRIT 2
  20 +#define UNKN 3
  21 +
  22 +#define VERSION "1.2"
  23 +
  24 +#define SMARTCTL_CMD_ATTRS "/usr/sbin/smartctl -A "
  25 +#define SMARTCTL_CMD_INFO "/usr/sbin/smartctl -i "
  26 +
  27 +#define ROTATION_INFO_STR "Rotation Rate:"
  28 +#define SSD_DEV_STR "Solid State Device"
  29 +
  30 +#define REALLOC_SEC_COUNT_ID 5
  31 +#define CURRENT_PENDING_SEC_ID 197
  32 +#define OFFLINE_UNCORRECT_ID 198
  33 +#define WEAR_COUNT_ID 177
  34 +#define RUNTIME_BAD_BLOCKS_IDD 183
  35 +#define REP_UNCORRECT_ID 187
  36 +
  37 +#define HDD 0
  38 +#define SSD 1
18 39
19 using namespace std; 40 using namespace std;
20 41
21 -int evalStatus(char* disk, string reallocatedCount, string currentPending, string offlineUncorrectable, string *status); 42 +struct SMARTAttr
  43 +{
  44 + string name;
  45 + int value;
  46 + int severity;
  47 +}; typedef struct SMARTAttr SMARTAttr;
  48 +
  49 +
  50 +map<int,SMARTAttr> prepareAttrMap(int driveType);
  51 +int getSmartAttrValue(string line);
  52 +int getSmartAttrID(string line);
  53 +int run_smartctl_cmd(const string command, const char* disk, string* output);
  54 +int checkDriveType(const char* disk);
  55 +int evalStatus(const char* disk, int driveType, string* status);
22 void printVersion(); 56 void printVersion();
23 void printHelp(bool longVersion); 57 void printHelp(bool longVersion);
  58 +void set_timeout(unsigned int sec);
24 59
25 #endif 60 #endif