Blame view

check_smart/check_smart.cpp 6.66 KB
Imanol-Mikel Barba Sabariego authored
1
2
#include "check_smart.h"
3
const char *servicename = (const char*)"SMART";
Imanol-Mikel Barba Sabariego authored
4
5
int getSmartAttrValue(string line) {
6
7
8
  return stoi(line.substr(line.find_last_of(" ")+1));
}
9
int getSmartAttrID(string line) {
10
11
12
  size_t first = line.find_first_not_of(' ');
  size_t last = line.find_first_of(' ',first);
  int id = -1;
13
14
15
16
17
18
  try {
    id = stoi(line.substr(first,last-first));
  }
  catch(...){
    return -1;
  }
19
20
21
  return id;
}
22
int checkDriveType(const char* disk) {
23
24
25
26
27
  string output = "";
  string RRLine = "";
  string line= "";

  int rc = run_smartctl_cmd(string(SMARTCTL_CMD_INFO), disk,&output);
28
  if(rc) {
29
30
31
32
33
34
    cout << "Error reading SMART data from disk " << disk << endl;
    exit(3);
  }

  stringstream sstream;
  sstream.str(output);
35
  while(getline(sstream,line) && RRLine == "") {
36
    size_t found = line.find(ROTATION_INFO_STR);
37
38
39
    if(found != string::npos) {
      RRLine = line;
    }
40
  }
41
  if(RRLine != "") {
42
    size_t found = RRLine.find(SSD_DEV_STR);
43
44
45
    if(found != string::npos) {
      return SSD;
    }
46
47
48
49
  }
  return HDD;
}
50
map<int,SMARTAttr> prepareAttrMap(int driveType) {
51
52
53
54
55
56
  map<int,SMARTAttr> map;

  SMARTAttr realloc;
  realloc.name = "Reallocated_Sector_Ct";
  realloc.value = -1;
  realloc.severity = WARN;
57
  realloc.optional = false;
58
59
60
61
62
  map[REALLOC_SEC_COUNT_ID] = realloc;

  SMARTAttr pending;
  SMARTAttr off_uncorrect;
  SMARTAttr wear;
63
  SMARTAttr wearout;
64
65
66
  SMARTAttr badblocks;
  SMARTAttr rep_uncorrect;
67
  switch(driveType) {
68
69
70
71
    case HDD:
      pending.name = "Current_Pending_Sector";
      pending.value = -1;
      pending.severity = CRIT;
72
      pending.optional = false;
73
74
75
76
77
      map[CURRENT_PENDING_SEC_ID] = pending;

      off_uncorrect.name = "Offline_Uncorrectable";
      off_uncorrect.value = -1;
      off_uncorrect.severity = CRIT;
78
      off_uncorrect.optional = false;
79
80
81
82
83
84
85
      map[OFFLINE_UNCORRECT_ID] = off_uncorrect;

      break;
    case SSD:
      wear.name = "Wear_Leveling_Count";
      wear.value = -1;
      wear.severity = WARN;
86
      wear.optional = true;
87
88
      map[WEAR_COUNT_ID] = wear;
89
90
91
92
93
94
      wearout.name = "Media_Wearout_Indicator";
      wearout.value = -1;
      wearout.severity = WARN;
      wearout.optional = true;
      map[WEAR_COUNT_ID] = wearout;
95
96
97
      badblocks.name = "Runtime_Bad_Block";
      badblocks.value = -1;
      badblocks.severity = CRIT;
98
      badblocks.optional = false;
99
100
101
102
103
      map[RUNTIME_BAD_BLOCKS_IDD] = badblocks;

      rep_uncorrect.name = "Reported_Uncorrect";
      rep_uncorrect.value = -1;
      rep_uncorrect.severity = CRIT;
104
      rep_uncorrect.optional = false;
105
106
107
108
109
110
111
      map[REP_UNCORRECT_ID] = rep_uncorrect;

      break;
  }
  return map;
}
112
int evalStatus(const char* disk, int driveType, string *status) {
113
114
115
116
  string output = "";
  string line = "";

  int rc = run_smartctl_cmd(string(SMARTCTL_CMD_ATTRS), disk,&output);
117
  if(rc) {
118
119
120
121
122
123
124
125
    cout << "Error reading SMART data from disk " << disk << endl;
    exit(UNKN);
  }

  map<int,SMARTAttr> attrMap = prepareAttrMap(driveType);

  stringstream sstream;
  sstream.str(output);
126
127
  while(getline(sstream,line)) {
    for(map<int,SMARTAttr>::iterator it = attrMap.begin(); it != attrMap.end(); ++it) {
128
      int id = it->first;
129
130
131
132
133
134
135
      int attrID = getSmartAttrID(line);
      if(attrID == -1) {
        continue;
      }
      if(getSmartAttrID(line) == id) {
        attrMap[id].value = getSmartAttrValue(line);
      }
136
137
138
139
    }
  }
  int ret = OK;
  *status = string(disk);
140
  for(map<int,SMARTAttr>::iterator it = attrMap.begin(); it != attrMap.end(); ++it) {
141
142
143
    int id = it->first;
    SMARTAttr attr = it->second;
144
145
146
147
    if(attr.value == -1) {
      if(attr.optional) {
        continue;
      }
148
149
150
151
      *status = string(disk) + " status UNKNOWN";
      return UNKN;
    }
    int veredict = 0;
152
153
154
155
    if(attr.value) {
      veredict = attr.severity;
    }
    switch(veredict) {
156
157
158
      case OK:
        break;
      case WARN:
159
160
161
        if(ret == OK) {
          ret = WARN;
        }
162
163
164
165
166
167
168
169
170
171
        break;
      case CRIT:
        ret = CRIT;
        break;
    }
    *status += " " + attr.name + ":" + to_string(attr.value);
  }
  return ret;
}
172
int run_smartctl_cmd(const string command, const char* disk, string* output) {
173
174
175
176
177
178
179
  int uid = getuid();
  setreuid(0,0);

  int rc = exec(command + disk,output);

  setreuid(uid,0);
  return rc;
Imanol-Mikel Barba Sabariego authored
180
181
}
182
void printVersion() {
183
  cout << "check_smart v" << VERSION << endl << endl;
Imanol-Mikel Barba Sabariego authored
184
185
}
186
187
void printHelp(bool longVersion) {
  if(longVersion) {
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
    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;
}
203
void set_timeout(unsigned int sec) {
204
205
206
207
208
209
210
211
212
213
214
  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
215
216
}
217
int main(int argc, char **argv) {
218
219
  set_timeout(10);
  int c;
220
221
  while ((c = getopt (argc, argv, "Vh")) != -1) {
    switch(c) {
222
223
224
225
226
227
228
229
230
231
232
233
      case 'h':
        printHelp(true);
	return OK;
      case 'V':
        printVersion();
        return OK;
      case '?':
        printHelp(false);
        return UNKN;
    }
  }
234
  if(argc == 1) {
235
236
237
238
239
240
241
    cout << "No disks checked" << endl;
    return 3;
  }

  string *results = new string[argc-1];
  int returnCode = OK;
242
  for(int i = 1; i < argc; i++) {
243
    int driveType = checkDriveType(argv[i]);
244
    switch(driveType) {
245
246
247
248
249
250
251
252
253
      case HDD:
      case SSD:
        break;
      default:
        cout << "Unknown disk type" << endl;
        return UNKN;
        break;
    }
254
    switch(evalStatus(argv[i],driveType,&(results[i-1]))) {
255
256
257
258
259
260
261
262
      case OK:
        break;
      case WARN:
        if(returnCode == OK) {returnCode = WARN;}
        break;
      case CRIT:
        returnCode = CRIT;
        break;
263
264
265
      case UNKN:
        returnCode = UNKN;
        break;
266
267
268
269
270
271
272
273
    }
  }

  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: ";}
274
  for(int i = 0; i < (argc-1); i++) {
275
276
277
278
279
    cout << results[i];
    if(i != (argc-2)) {cout << ", ";}
  }
  cout << endl;
  return returnCode;
Imanol-Mikel Barba Sabariego authored
280
}