|
1
|
#include "check_smart.h"
|
|
2
|
#include <regex>
|
|
3
|
|
|
4
|
const char *servicename = (const char*)"SMART";
|
|
5
|
|
|
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
int getSmartAttrValue(string line, unsigned int col) {
line = std::regex_replace(line, std::regex("\\s+"), " ");
line = std::regex_replace(line, std::regex("^ "), "");
// Find nth col
size_t pos = 0;
int cnt = 0;
while(cnt != col) {
pos = line.find(" ", pos+1);
if (pos == std::string::npos) {
throw std::runtime_error("Column out of range");
}
cnt++;
}
if(pos != 0) {
++pos;
}
size_t end_pos = line.find(" ", pos);
if(end_pos != std::string::npos) {
return stoi(line.substr(pos, line.find(" ", pos)));
}
return stoi(line.substr(pos));
|
|
30
31
|
}
|
|
32
|
int getSmartAttrID(string line) {
|
|
33
34
35
|
size_t first = line.find_first_not_of(' ');
size_t last = line.find_first_of(' ',first);
int id = -1;
|
|
36
37
38
39
40
41
|
try {
id = stoi(line.substr(first,last-first));
}
catch(...){
return -1;
}
|
|
42
43
44
|
return id;
}
|
|
45
46
47
48
49
50
51
52
53
|
std::string getSmartAttrName(std::string line) {
size_t first = line.find_first_not_of(' ', line.find_first_of(' ',line.find_first_not_of(' ')));
size_t last = line.find_first_of(' ', first);
if(first == std::string::npos || last == std::string::npos) {
return "";
}
return line.substr(first,last-first);
}
|
|
54
|
int checkDriveType(const char* disk) {
|
|
55
56
57
58
59
|
string output = "";
string RRLine = "";
string line= "";
int rc = run_smartctl_cmd(string(SMARTCTL_CMD_INFO), disk,&output);
|
|
60
|
if(rc) {
|
|
61
62
63
64
65
66
|
cout << "Error reading SMART data from disk " << disk << endl;
exit(3);
}
stringstream sstream;
sstream.str(output);
|
|
67
|
while(getline(sstream,line) && RRLine == "") {
|
|
68
|
size_t found = line.find(ROTATION_INFO_STR);
|
|
69
70
71
|
if(found != string::npos) {
RRLine = line;
}
|
|
72
|
}
|
|
73
|
if(RRLine != "") {
|
|
74
|
size_t found = RRLine.find(SSD_DEV_STR);
|
|
75
76
77
|
if(found != string::npos) {
return SSD;
}
|
|
78
79
80
81
|
}
return HDD;
}
|
|
82
83
84
85
86
|
map<std::string, SMARTAttr*> prepareAttrMap(int driveType) {
map<std::string, SMARTAttr*> map;
map[REALLOC_SEC_COUNT_NAME] = &reallocated;
map[REP_UNCORRECT_NAME] = &rep_uncorrect;
map[UNCORRECT_COUNT_NAME] = &rep_uncorrect;
|
|
87
|
|
|
88
|
switch(driveType) {
|
|
89
|
case HDD:
|
|
90
91
|
map[CURRENT_PENDING_SEC_NAME] = &pending;
map[OFFLINE_UNCORRECT_NAME] = &off_uncorrect;
|
|
92
|
break;
|
|
93
|
case SSD:
|
|
94
95
96
|
map[WEAR_COUNT_NAME] = &wear;
map[MEDIA_WEAROUT_NAME] = &wearout;
map[RUNTIME_BAD_BLOCKS_NAME] = &badblocks;
|
|
97
|
break;
|
|
98
99
100
101
|
}
return map;
}
|
|
102
|
int evalStatus(const char* disk, int driveType, string *status) {
|
|
103
104
105
106
|
string output = "";
string line = "";
int rc = run_smartctl_cmd(string(SMARTCTL_CMD_ATTRS), disk,&output);
|
|
107
|
if(rc) {
|
|
108
109
110
111
|
cout << "Error reading SMART data from disk " << disk << endl;
exit(UNKN);
}
|
|
112
|
map<std::string, SMARTAttr*> attrMap = prepareAttrMap(driveType);
|
|
113
114
115
|
stringstream sstream;
sstream.str(output);
|
|
116
|
while(getline(sstream,line)) {
|
|
117
118
119
120
|
for(map<std::string, SMARTAttr*>::iterator it = attrMap.begin(); it != attrMap.end(); ++it) {
std::string name = it->first;
std::string attrName = getSmartAttrName(line);
if(attrName == "") {
|
|
121
122
|
continue;
}
|
|
123
124
|
if(attrName == name) {
attrMap[name]->value = getSmartAttrValue(line, attrMap[name]->col);
|
|
125
|
}
|
|
126
127
128
129
|
}
}
int ret = OK;
*status = string(disk);
|
|
130
131
132
|
for(map<std::string, SMARTAttr*>::iterator it = attrMap.begin(); it != attrMap.end(); ++it) {
std::string name = it->first;
SMARTAttr* attr = it->second;
|
|
133
|
|
|
134
135
|
if(attr->value == -1) {
if(attr->optional) {
|
|
136
137
|
continue;
}
|
|
138
139
140
|
*status = string(disk) + " status UNKNOWN";
return UNKN;
}
|
|
141
|
|
|
142
|
int veredict = 0;
|
|
143
|
|
|
144
145
|
if(attr->lower_than) {
if(attr->value < attr->threshold_warn) {
|
|
146
147
|
veredict = WARN;
}
|
|
148
|
if(attr->threshold_crit != -1 && attr->value < attr->threshold_crit) {
|
|
149
150
151
|
veredict = CRIT;
}
} else {
|
|
152
|
if(attr->value > attr->threshold_warn) {
|
|
153
154
|
veredict = WARN;
}
|
|
155
|
if(attr->threshold_crit != -1 && attr->value > attr->threshold_crit) {
|
|
156
157
|
veredict = CRIT;
}
|
|
158
159
|
}
|
|
160
|
switch(veredict) {
|
|
161
162
163
|
case OK:
break;
case WARN:
|
|
164
165
166
|
if(ret == OK) {
ret = WARN;
}
|
|
167
168
169
170
171
|
break;
case CRIT:
ret = CRIT;
break;
}
|
|
172
|
*status += " " + attr->name + ":" + to_string(attr->value);
|
|
173
174
175
176
|
}
return ret;
}
|
|
177
|
int run_smartctl_cmd(const string command, const char* disk, string* output) {
|
|
178
179
180
181
182
183
184
|
int uid = getuid();
setreuid(0,0);
int rc = exec(command + disk,output);
setreuid(uid,0);
return rc;
|
|
185
186
|
}
|
|
187
|
void printVersion() {
|
|
188
|
cout << "check_smart v" << VERSION << endl << endl;
|
|
189
190
|
}
|
|
191
192
|
void printHelp(bool longVersion) {
if(longVersion) {
|
|
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
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;
}
|
|
208
|
void set_timeout(unsigned int sec) {
|
|
209
210
211
212
213
214
215
216
217
218
219
|
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);
|
|
220
221
|
}
|
|
222
|
int main(int argc, char **argv) {
|
|
223
224
|
set_timeout(10);
int c;
|
|
225
226
|
while ((c = getopt (argc, argv, "Vh")) != -1) {
switch(c) {
|
|
227
228
|
case 'h':
printHelp(true);
|
|
229
|
return OK;
|
|
230
231
232
233
234
235
236
237
238
|
case 'V':
printVersion();
return OK;
case '?':
printHelp(false);
return UNKN;
}
}
|
|
239
|
if(argc == 1) {
|
|
240
241
242
243
244
245
246
|
cout << "No disks checked" << endl;
return 3;
}
string *results = new string[argc-1];
int returnCode = OK;
|
|
247
|
for(int i = 1; i < argc; i++) {
|
|
248
|
int driveType = checkDriveType(argv[i]);
|
|
249
|
switch(driveType) {
|
|
250
251
252
253
254
255
256
257
258
|
case HDD:
case SSD:
break;
default:
cout << "Unknown disk type" << endl;
return UNKN;
break;
}
|
|
259
|
switch(evalStatus(argv[i],driveType,&(results[i-1]))) {
|
|
260
261
262
263
264
265
266
267
|
case OK:
break;
case WARN:
if(returnCode == OK) {returnCode = WARN;}
break;
case CRIT:
returnCode = CRIT;
break;
|
|
268
269
270
|
case UNKN:
returnCode = UNKN;
break;
|
|
271
272
273
274
275
276
277
278
|
}
}
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: ";}
|
|
279
|
for(int i = 0; i < (argc-1); i++) {
|
|
280
281
282
283
284
|
cout << results[i];
if(i != (argc-2)) {cout << ", ";}
}
cout << endl;
return returnCode;
|
|
285
|
}
|