Blame view

check_smart/check_smart.cpp 6.91 KB
Imanol-Mikel Barba Sabariego authored
1
#include "check_smart.h"
2
#include <regex>
Imanol-Mikel Barba Sabariego authored
3
4
const char *servicename = (const char*)"SMART";
Imanol-Mikel Barba Sabariego authored
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;
}
Imanol-Mikel Barba Sabariego authored
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;
}
Imanol-Mikel Barba Sabariego authored
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:
Imanol-Mikel Barba Sabariego authored
90
91
      map[CURRENT_PENDING_SEC_NAME] = &pending;
      map[OFFLINE_UNCORRECT_NAME] = &off_uncorrect;
Imanol-Mikel Barba Sabariego authored
92
    break;
93
    case SSD:
Imanol-Mikel Barba Sabariego authored
94
95
96
      map[WEAR_COUNT_NAME] = &wear;
      map[MEDIA_WEAROUT_NAME] = &wearout;
      map[RUNTIME_BAD_BLOCKS_NAME] = &badblocks;
Imanol-Mikel Barba Sabariego authored
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);
  }
Imanol-Mikel Barba Sabariego authored
112
  map<std::string, SMARTAttr*> attrMap = prepareAttrMap(driveType);
113
114
115

  stringstream sstream;
  sstream.str(output);
116
  while(getline(sstream,line)) {
Imanol-Mikel Barba Sabariego authored
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;
      }
Imanol-Mikel Barba Sabariego authored
123
124
      if(attrName == name) {
        attrMap[name]->value = getSmartAttrValue(line, attrMap[name]->col);
125
      }
126
127
128
129
    }
  }
  int ret = OK;
  *status = string(disk);
Imanol-Mikel Barba Sabariego authored
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
Imanol-Mikel Barba Sabariego authored
134
135
    if(attr->value == -1) {
      if(attr->optional) {
136
137
        continue;
      }
138
139
140
      *status = string(disk) + " status UNKNOWN";
      return UNKN;
    }
Imanol-Mikel Barba Sabariego authored
141
142
    int veredict = 0;
143
Imanol-Mikel Barba Sabariego authored
144
145
    if(attr->lower_than) {
      if(attr->value < attr->threshold_warn) {
146
147
        veredict = WARN;
      }
Imanol-Mikel Barba Sabariego authored
148
      if(attr->threshold_crit != -1 && attr->value < attr->threshold_crit) {
149
150
151
        veredict = CRIT;
      }
    } else {
Imanol-Mikel Barba Sabariego authored
152
      if(attr->value > attr->threshold_warn) {
153
154
        veredict = WARN;
      }
Imanol-Mikel Barba Sabariego authored
155
      if(attr->threshold_crit != -1 && attr->value > attr->threshold_crit) {
156
157
        veredict = CRIT;
      }
Imanol-Mikel Barba Sabariego authored
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;
    }
Imanol-Mikel Barba Sabariego authored
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;
Imanol-Mikel Barba Sabariego authored
185
186
}
187
void printVersion() {
188
  cout << "check_smart v" << VERSION << endl << endl;
Imanol-Mikel Barba Sabariego authored
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);
Imanol-Mikel Barba Sabariego authored
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;
Imanol-Mikel Barba Sabariego authored
285
}