Commit f3a48aed56edc3f5053f68421dbc764d5930797f

Authored by Imanol-Mikel Barba Sabariego
1 parent 1272a895

Improved check_smart to account for SSDs and made overall code structure better

check_smart/auxiliar.cpp
... ... @@ -52,4 +52,4 @@ int exec(string cmd, string *output)
52 52 }
53 53 }
54 54 return pclose(pipe)/256;
55   -}
56 55 \ No newline at end of file
  56 +}
... ...
check_smart/auxiliar.h
... ... @@ -15,7 +15,7 @@
15 15  
16 16 using namespace std;
17 17  
18   -extern char *servicename;
  18 +extern const char *servicename;
19 19  
20 20 int str2int(string str);
21 21 string int2str(int x);
... ...
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
... ...