Commit f3a48aed56edc3f5053f68421dbc764d5930797f
1 parent
1272a895
Improved check_smart to account for SSDs and made overall code structure better
Showing
4 changed files
with
301 additions
and
190 deletions
check_smart/auxiliar.cpp
check_smart/auxiliar.h
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 argv[i]" << 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 | + int gid = getgid(); | ||
157 | + setuid(0); | ||
158 | + setgid(0); | ||
159 | + | ||
160 | + int rc = exec(command + disk,output); | ||
161 | + | ||
162 | + setuid(uid); | ||
163 | + setgid(gid); | ||
164 | + | ||
165 | + return rc; | ||
28 | } | 166 | } |
29 | 167 | ||
30 | void printVersion() | 168 | void printVersion() |
31 | { | 169 | { |
32 | - cout << "check_smart v" << VERSION << endl << endl; | 170 | + cout << "check_smart v" << VERSION << endl << endl; |
33 | } | 171 | } |
34 | 172 | ||
35 | void printHelp(bool longVersion) | 173 | void printHelp(bool longVersion) |
36 | { | 174 | { |
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; | 175 | + if(longVersion) |
176 | + { | ||
177 | + printVersion(); | ||
178 | + 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; | ||
179 | + printHelp(false); | ||
180 | + cout << "Options:" << endl; | ||
181 | + cout << " -h" << endl; | ||
182 | + cout << " Print detailed help screen" << endl; | ||
183 | + cout << " -V" << endl; | ||
184 | + cout << " Print version information" << endl; | ||
185 | + cout << " DISKS" << endl; | ||
186 | + cout << " Disks for which to retrieve S.M.A.R.T data" << endl << endl; | ||
187 | + return; | ||
188 | + } | ||
189 | + cout << "Usage: " << endl << "check_smart [-hV] DISKS..." << endl << endl; | ||
190 | +} | ||
191 | + | ||
192 | +void set_timeout(unsigned int sec) | ||
193 | +{ | ||
194 | + struct itimerval timer; | ||
195 | + timer.it_value.tv_sec = sec; | ||
196 | + timer.it_value.tv_usec = 0; | ||
197 | + timer.it_interval.tv_sec = 0; | ||
198 | + timer.it_interval.tv_usec = 0; | ||
199 | + setitimer (ITIMER_VIRTUAL, &timer, 0); | ||
200 | + | ||
201 | + struct sigaction sa; | ||
202 | + memset (&sa, 0, sizeof (sa)); | ||
203 | + sa.sa_handler = &timer_handler; | ||
204 | + sigaction (SIGVTALRM, &sa, 0); | ||
205 | + | ||
52 | } | 206 | } |
53 | 207 | ||
54 | int main(int argc, char **argv) | 208 | int main(int argc, char **argv) |
55 | { | 209 | { |
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; | 210 | + set_timeout(10); |
211 | + int c; | ||
212 | + while ((c = getopt (argc, argv, "Vh")) != -1) | ||
213 | + { | ||
214 | + switch(c) | ||
215 | + { | ||
216 | + case 'h': | ||
217 | + printHelp(true); | ||
218 | + return OK; | ||
219 | + case 'V': | ||
220 | + printVersion(); | ||
221 | + return OK; | ||
222 | + case '?': | ||
223 | + printHelp(false); | ||
224 | + return UNKN; | ||
225 | + } | ||
226 | + } | ||
227 | + | ||
228 | + if(argc == 1) | ||
229 | + { | ||
230 | + cout << "No disks checked" << endl; | ||
231 | + return 3; | ||
232 | + } | ||
233 | + | ||
234 | + string *results = new string[argc-1]; | ||
235 | + int returnCode = OK; | ||
236 | + | ||
237 | + for(int i = 1; i < argc; i++) | ||
238 | + { | ||
239 | + int driveType = checkDriveType(argv[i]); | ||
240 | + switch(driveType) | ||
241 | + { | ||
242 | + case HDD: | ||
243 | + case SSD: | ||
244 | + break; | ||
245 | + default: | ||
246 | + cout << "Unknown disk type" << endl; | ||
247 | + return UNKN; | ||
248 | + break; | ||
249 | + } | ||
250 | + | ||
251 | + switch(evalStatus(argv[i],driveType,&(results[i-1]))) | ||
252 | + { | ||
253 | + case OK: | ||
254 | + break; | ||
255 | + case WARN: | ||
256 | + if(returnCode == OK) {returnCode = WARN;} | ||
257 | + break; | ||
258 | + case CRIT: | ||
259 | + returnCode = CRIT; | ||
260 | + break; | ||
261 | + } | ||
262 | + } | ||
263 | + | ||
264 | + cout << servicename; | ||
265 | + if(returnCode == OK) {cout << " OK - disk status: ";} | ||
266 | + else if(returnCode == WARN) {cout << " WARNING - disk status: ";} | ||
267 | + else if(returnCode == CRIT) {cout << " CRITICAL - disk status: ";} | ||
268 | + else if(returnCode == UNKN) {cout << " UNKNOWN - disk status: ";} | ||
269 | + for(int i = 0; i < (argc-1); i++) | ||
270 | + { | ||
271 | + cout << results[i]; | ||
272 | + if(i != (argc-2)) {cout << ", ";} | ||
273 | + } | ||
274 | + cout << endl; | ||
275 | + return returnCode; | ||
200 | } | 276 | } |
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 |