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 | 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 | 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 | 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 | 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 | 3 | |
4 | 4 | #include <iostream> |
5 | 5 | #include <sstream> |
6 | +#include <map> | |
6 | 7 | |
7 | 8 | #include <stdio.h> |
8 | -#include <cstring> | |
9 | -#include <cstdlib> | |
9 | +#include <string.h> | |
10 | +#include <stdlib.h> | |
10 | 11 | #include <unistd.h> |
11 | 12 | |
12 | 13 | #include <sys/time.h> |
13 | 14 | |
14 | 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 | 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 | 56 | void printVersion(); |
23 | 57 | void printHelp(bool longVersion); |
58 | +void set_timeout(unsigned int sec); | |
24 | 59 | |
25 | 60 | #endif | ... | ... |