├── .gitignore ├── Makefile ├── README.md ├── ata.h ├── check_scsi_smart.cc ├── debian ├── changelog ├── compat ├── control ├── copyright ├── rules └── source │ └── format ├── endian.h ├── nagios-plugin-check-scsi-smart.spec ├── scsi.h ├── smart.cc └── smart.h /.gitignore: -------------------------------------------------------------------------------- 1 | check_scsi_smart 2 | debian/nagios-plugin-check-scsi-smart 3 | *.o 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CXXFLAGS=-O2 -Wall 3 | LDFLAGS=-O2 -Wall 4 | EXE=check_scsi_smart 5 | SOURCE=$(wildcard *.cc) 6 | OBJECT=$(patsubst %.cc,%.o,$(SOURCE)) 7 | PREFIX=/usr 8 | LIBDIR=lib 9 | 10 | 11 | all: $(EXE) 12 | 13 | $(EXE): $(OBJECT) 14 | $(CXX) $(LDFLAGS) -o $@ $(OBJECT) 15 | 16 | %.o: %.cc 17 | $(CXX) $(CXXFLAGS) -c -o $@ $< 18 | 19 | install: 20 | mkdir -p ${DESTDIR}${PREFIX}/${LIBDIR}/nagios/plugins 21 | install -m 0755 ${EXE} ${DESTDIR}${PREFIX}/${LIBDIR}/nagios/plugins 22 | 23 | .PHONY: clean 24 | clean: 25 | rm -f *.o 26 | rm -f $(EXE) 27 | 28 | # vi: noet: 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nagios SMART Drive Checker 2 | 3 | ## Description 4 | 5 | Uses SCSI commands to tunnel SMART checks to ATA hard drives. Unlike the 6 | venerable check\_ide\_smart check this will work on all modern devices 7 | even those behind SAS HBAs or expanders. It will also monitor for SMART 8 | error logs which may indicate failure when base SMART attributes do not. 9 | 10 | As of version 1.1.0 the API has changed. The check no longer emits verbose 11 | output, this functionality is delegated to smartctl, however it does provide 12 | all raw values as performance data. This is the first step in providing 13 | true predictive failure. This data can be monitored via a Graphite writer 14 | plugin and thresholds explicitly set for your particular device/environment. 15 | 16 | ## Prerequisites 17 | 18 | * g++ 19 | * gmake 20 | 21 | ## Building 22 | 23 | make 24 | 25 | ## Usage 26 | 27 | ### Help 28 | 29 | check_scsi_smart v1.2.x 30 | (C) 2015-2016 Simon Murray 31 | 32 | Usage: 33 | check_scsi_smart [-d ] 34 | 35 | Options: 36 | -h, --help 37 | Print detailed help 38 | -V, --version 39 | Print version information 40 | -d, --device=DEVICE 41 | Select device DEVICE 42 | -w, --warning=ID:THRESHOLD[,ID:THRESHOLD] 43 | Specify warning thresholds as a list of integer attributes to integer thresholds 44 | -c, --critical=ID:THRESHOLD[,ID:THRESHOLD] 45 | Specify critical thresholds as a list of integer attributes to integer thresholds 46 | 47 | ### Output 48 | 49 | $ sudo ./check_scsi_smart -d /dev/sdc -w 1:1000,3:1000 -c 187:1 50 | CRITICAL: prdfail 0, advisory 0, critical 1, warning 1, logs 2 | 1_read_error_rate=151669074;1000;;; 3_spin_up_time=0;1000;;; 4_start_stop_count=26;;;; 5_reallocated_sectors_count=10904;;;; 7_seek_error_rate=8645237955;;;; 9_power_on_hours=23052;;;; 10_spin_retry_count=0;;;; 12_power_cycle_count=25;;;; 183_sata_downshift_error_count=124;;;; 184_end_to_end_error=0;;;; 187_reported_uncorrectable_errors=2;;1;; 188_command_timeout=4295032833;;;; 189_high_fly_writes=1;;;; 190_airflow_temperature=23;;;; 191_g_sense_error_rate=0;;;; 192_power_off_retract_count=18;;;; 193_load_cycle_count=8823;;;; 194_temperature=23;;;; 197_current_pending_sector_count=4288;;;; 198_uncorrectable_sector_count=4288;;;; 199_ultradma_crc_error_count=0;;;; 240_flying_head_hours=22723;;;; 241_total_lbas_written=4595646719;;;; 242_total_lbas_read=1956891669;;;; 51 | -------------------------------------------------------------------------------- /ata.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SMART Nagios/Icinga Disk Check 3 | * ------------------------------ 4 | * 5 | * License 6 | * ------- 7 | * (C) 2015-2017 Simon Murray 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | #ifndef _ata_H_ 24 | #define _ata_H_ 25 | 26 | #include 27 | 28 | /* ATA commands */ 29 | const uint8_t ATA_IDENTIFY_DEVICE = 0xec; 30 | const uint8_t ATA_SMART = 0xb0; 31 | 32 | /* ATA protocols */ 33 | const uint8_t ATA_PROTOCOL_PIO_DATA_IN = 0x4; 34 | 35 | /* ATA transfer direction */ 36 | const uint8_t ATA_TRANSFER_DIRECTION_TO_DEVICE = 0x0; 37 | const uint8_t ATA_TRANSFER_DIRECTION_FROM_DEVICE = 0x1; 38 | 39 | /* ATA block transfer mode */ 40 | const uint8_t ATA_TRANSFER_SIZE_BYTE = 0x0; 41 | const uint8_t ATA_TRANSFER_SIZE_BLOCK = 0x1; 42 | 43 | /* ATA block transfer type */ 44 | const uint8_t ATA_TRANSFER_TYPE_SECTOR = 0x0; 45 | const uint8_t ATA_TRANSFER_TYPE_LOGICAL_SECTOR = 0x1; 46 | 47 | /* ATA transfer length location */ 48 | const uint8_t ATA_TRANSFER_LENGTH_NONE = 0x0; 49 | const uint8_t ATA_TRANSFER_LENGTH_FEATURES = 0x1; 50 | const uint8_t ATA_TRANSFER_LENGTH_COUNT = 0x2; 51 | const uint8_t ATA_TRANSFER_LENGTH_TPSIU = 0x3; 52 | 53 | /* ATA Log Addresses */ 54 | const uint8_t ATA_LOG_ADDRESS_DIRECTORY = 0x0; 55 | const uint8_t ATA_LOG_ADDRESS_SMART = 0x1; 56 | 57 | #endif//_ata_H_ 58 | -------------------------------------------------------------------------------- /check_scsi_smart.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SMART Nagios/Icinga Disk Check 3 | * ------------------------------ 4 | * 5 | * License 6 | * ------- 7 | * (C) 2015-2017 Simon Murray 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | * Description 23 | * ----------- 24 | * Checks ATA devices for failures via SMART disk checks. Unlike the old 25 | * and flawed check_ide_smart this check uses the SCSI protocol to access 26 | * drives. This allows the SCSI command to be translated by the relevant 27 | * SAT in the IO chain, be it linux's libata for SATA controllers, an HBA 28 | * for direct attached SAS controllers or SAS expander. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "scsi.h" 42 | #include "ata.h" 43 | #include "smart.h" 44 | #include "endian.h" 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | using namespace std; 55 | 56 | const char* const BINARY = "check_scsi_smart"; 57 | const char* const VERSION = "1.2.3"; 58 | 59 | // Nagios return codes 60 | const int NAGIOS_OK = 0; 61 | const int NAGIOS_WARNING = 1; 62 | const int NAGIOS_CRITICAL = 2; 63 | const int NAGIOS_UNKNOWN = 3; 64 | 65 | const size_t SECTOR_SIZE = 512; 66 | 67 | // Mapping to hold attribute -> threshold data 68 | typedef map SmartThresholdMap; 69 | 70 | /* 71 | * Function: version 72 | * ----------------- 73 | * Print out the version string 74 | */ 75 | void version() { 76 | 77 | cout << BINARY << " v" << VERSION << endl; 78 | 79 | } 80 | 81 | /* 82 | * Function: usage 83 | * --------------- 84 | * Print out the usage syntax 85 | */ 86 | void usage() { 87 | 88 | cout << "Usage:" << endl 89 | << BINARY << " [-d ]" << endl; 90 | 91 | } 92 | 93 | /* 94 | * Function: help 95 | * -------------- 96 | * Print out the verbose help screen 97 | */ 98 | void help() { 99 | 100 | version(); 101 | 102 | cout << "(C) 2015-2016 Simon Murray " << endl 103 | << endl; 104 | 105 | usage(); 106 | 107 | cout << endl 108 | << "Options:" << endl 109 | << "-h, --help" << endl 110 | << " Print detailed help" << endl 111 | << "-V, --version" << endl 112 | << " Print version information" << endl 113 | << "-d, --device=DEVICE" << endl 114 | << " Select device DEVICE" << endl 115 | << "-w, --warning=ID:THRESHOLD[,ID:THRESHOLD]" << endl 116 | << " Specify warning thresholds as a list of integer attributes to integer thresholds" << endl 117 | << "-c, --critical=ID:THRESHOLD[,ID:THRESHOLD]" << endl 118 | << " Specify critical thresholds as a list of integer attributes to integer thresholds" << endl 119 | << endl; 120 | 121 | } 122 | 123 | /* 124 | * Function: sgio 125 | * -------------- 126 | * Sends a CDB to the target device and recieves a response 127 | * 128 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 129 | * cmdp: Pointer to a SCSI CDB 130 | * cmd_len: Length of the CDB 131 | * dxferp: Pointer to the SCSI data buffer 132 | * dxfer_len: Length of the SCSI data buffer 133 | */ 134 | bool sgio(int fd, unsigned char* cmdp, int cmd_len, unsigned char* dxferp, int dxfer_len) { 135 | 136 | sg_io_hdr_t sgio_hdr; 137 | unsigned char sense[32]; 138 | 139 | memset(&sgio_hdr, 0, sizeof(sg_io_hdr_t)); 140 | sgio_hdr.interface_id = 'S'; 141 | sgio_hdr.dxfer_direction = SG_DXFER_FROM_DEV; 142 | sgio_hdr.cmd_len = cmd_len; 143 | sgio_hdr.mx_sb_len = 32; 144 | sgio_hdr.dxfer_len = dxfer_len; 145 | sgio_hdr.dxferp = dxferp; 146 | sgio_hdr.cmdp = cmdp; 147 | sgio_hdr.sbp = sense; 148 | 149 | if(ioctl(fd, SG_IO, &sgio_hdr) < 0) { 150 | cerr << "UNKNOWN: SG_IO ioctl error" << endl; 151 | exit(NAGIOS_UNKNOWN); 152 | } 153 | 154 | return !sgio_hdr.status; 155 | 156 | } 157 | 158 | /* 159 | * Function: ata_identify 160 | * ---------------------- 161 | * Send an IDENTIFY command to the ATA device and recieve the data 162 | * 163 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 164 | * buf: Data buffer to receive the data into, must be at least SECTOR 165 | */ 166 | bool ata_identify(int fd, unsigned char* buf) { 167 | 168 | sbc_ata_pass_through ata_pass_through; 169 | memset(reinterpret_cast(&ata_pass_through), 0, sizeof(sbc_ata_pass_through)); 170 | 171 | ata_pass_through.operation_code = SBC_ATA_PASS_THROUGH; 172 | ata_pass_through.protocol = ATA_PROTOCOL_PIO_DATA_IN; 173 | ata_pass_through.t_dir = ATA_TRANSFER_DIRECTION_FROM_DEVICE; 174 | ata_pass_through.byte_block = ATA_TRANSFER_SIZE_BLOCK; 175 | ata_pass_through.t_type = ATA_TRANSFER_TYPE_SECTOR; 176 | ata_pass_through.t_length = ATA_TRANSFER_LENGTH_COUNT; 177 | ata_pass_through.count_7_0 = 1; 178 | ata_pass_through.command = ATA_IDENTIFY_DEVICE; 179 | 180 | return sgio(fd, reinterpret_cast(&ata_pass_through), sizeof(ata_pass_through), buf, SECTOR_SIZE); 181 | 182 | } 183 | 184 | /* 185 | * Function: ata_smart_read_data 186 | * ----------------------------- 187 | * Send a SMART READ DATA command to the ATA device and recieve the data 188 | * 189 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 190 | * buf: Data buffer to receive the data into, must be at least SECTOR 191 | */ 192 | bool ata_smart_read_data(int fd, unsigned char* buf) { 193 | 194 | sbc_ata_pass_through ata_pass_through; 195 | memset(reinterpret_cast(&ata_pass_through), 0, sizeof(sbc_ata_pass_through)); 196 | 197 | ata_pass_through.operation_code = SBC_ATA_PASS_THROUGH; 198 | ata_pass_through.protocol = ATA_PROTOCOL_PIO_DATA_IN; 199 | ata_pass_through.t_dir = ATA_TRANSFER_DIRECTION_FROM_DEVICE; 200 | ata_pass_through.byte_block = ATA_TRANSFER_SIZE_BLOCK; 201 | ata_pass_through.t_type = ATA_TRANSFER_TYPE_SECTOR; 202 | ata_pass_through.t_length = ATA_TRANSFER_LENGTH_COUNT; 203 | ata_pass_through.count_7_0 = 1; 204 | ata_pass_through.command = ATA_SMART; 205 | ata_pass_through.features_7_0 = SMART_READ_DATA; 206 | ata_pass_through.lba_23_16 = 0xc2; 207 | ata_pass_through.lba_15_8 = 0x4f; 208 | 209 | return sgio(fd, reinterpret_cast(&ata_pass_through), sizeof(ata_pass_through), buf, SECTOR_SIZE); 210 | 211 | } 212 | 213 | /* 214 | * Function: ata_smart_read_thresholds 215 | * ----------------------------------- 216 | * Send a SMART READ THRESHOLDS command to the ATA device and recieve the data 217 | * 218 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 219 | * buf: Data buffer to receive the data into, must be at least SECTOR 220 | */ 221 | bool ata_smart_read_thresholds(int fd, unsigned char* buf) { 222 | 223 | sbc_ata_pass_through ata_pass_through; 224 | memset(reinterpret_cast(&ata_pass_through), 0, sizeof(sbc_ata_pass_through)); 225 | 226 | ata_pass_through.operation_code = SBC_ATA_PASS_THROUGH; 227 | ata_pass_through.protocol = ATA_PROTOCOL_PIO_DATA_IN; 228 | ata_pass_through.t_dir = ATA_TRANSFER_DIRECTION_FROM_DEVICE; 229 | ata_pass_through.byte_block = ATA_TRANSFER_SIZE_BLOCK; 230 | ata_pass_through.t_type = ATA_TRANSFER_TYPE_SECTOR; 231 | ata_pass_through.t_length = ATA_TRANSFER_LENGTH_COUNT; 232 | ata_pass_through.count_7_0 = 1; 233 | ata_pass_through.command = ATA_SMART; 234 | ata_pass_through.features_7_0 = SMART_READ_THRESHOLDS; 235 | ata_pass_through.lba_23_16 = 0xc2; 236 | ata_pass_through.lba_15_8 = 0x4f; 237 | 238 | return sgio(fd, reinterpret_cast(&ata_pass_through), sizeof(ata_pass_through), buf, SECTOR_SIZE); 239 | 240 | } 241 | 242 | /* 243 | * Function: ata_smart_read_log 244 | * ---------------------------- 245 | * Send a SMART READ LOG command to the ATA device and receive the data 246 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 247 | * buf: Data buffer to receive the data into, must be at least sectors * SECTOR 248 | * bytes. 249 | * log: Log to read See A.1 for ATA8-ACS 250 | */ 251 | bool ata_smart_read_log(int fd, unsigned char* buf, int log, uint16_t sectors) { 252 | 253 | sbc_ata_pass_through ata_pass_through; 254 | memset(reinterpret_cast(&ata_pass_through), 0, sizeof(sbc_ata_pass_through)); 255 | 256 | ata_pass_through.operation_code = SBC_ATA_PASS_THROUGH; 257 | ata_pass_through.protocol = ATA_PROTOCOL_PIO_DATA_IN; 258 | ata_pass_through.t_dir = ATA_TRANSFER_DIRECTION_FROM_DEVICE; 259 | ata_pass_through.byte_block = ATA_TRANSFER_SIZE_BLOCK; 260 | ata_pass_through.t_type = ATA_TRANSFER_TYPE_SECTOR; 261 | ata_pass_through.t_length = ATA_TRANSFER_LENGTH_COUNT; 262 | ata_pass_through.count_15_8 = sectors >> 8; 263 | ata_pass_through.count_7_0 = sectors; 264 | ata_pass_through.command = ATA_SMART; 265 | ata_pass_through.features_7_0 = SMART_READ_LOG; 266 | ata_pass_through.lba_23_16 = 0xc2; 267 | ata_pass_through.lba_15_8 = 0x4f; 268 | ata_pass_through.lba_7_0 = log; 269 | 270 | return sgio(fd, reinterpret_cast(&ata_pass_through), sizeof(ata_pass_through), buf, sectors * SECTOR_SIZE); 271 | 272 | } 273 | 274 | /* 275 | * Function: ata_smart_read_log_directory 276 | * -------------------------------------- 277 | * Reads the SMART flog directory 278 | * fd: File descriptor pointing at a SCSI or SCSI generic device nod 279 | * buf: Data buffer to receive the data into, must be at least SECTOR 280 | */ 281 | bool ata_smart_read_log_directory(int fd, unsigned char* buf) { 282 | 283 | return ata_smart_read_log(fd, buf, ATA_LOG_ADDRESS_DIRECTORY, 1); 284 | 285 | } 286 | 287 | /* 288 | * Function: check_smart_attributes 289 | * -------------------------------- 290 | * Checks attributes against vendor thresholds 291 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 292 | * critical_thresholds: Map of atrribute IDs to raw thresholds 293 | * warning_thresholds: Map of atrribute IDs to raw thresholds 294 | * code: Reference to the current return code 295 | * prdfail: Reference to a counter of vendor predicted fails 296 | * advisory: Reference to a counter of vendor advisory end of life 297 | * crit: Reference to a counter of critical attributes 298 | * warn: Reference to a counter of advisory attributes 299 | * perfdata: Output stream to dump performance data to 300 | */ 301 | void check_smart_attributes(int fd, SmartThresholdMap& critical_thresholds, SmartThresholdMap& warning_thresholds, 302 | int& code, int& prdfail, int& advisory, int& crit, int& warn, ostream& perfdata) { 303 | 304 | // Load the SMART data and thresholds pages 305 | smart_data sd; 306 | ata_smart_read_data(fd, reinterpret_cast(&sd)); 307 | 308 | smart_thresholds st; 309 | ata_smart_read_thresholds(fd, reinterpret_cast(&st)); 310 | 311 | // Perform actual SMART threshold checks 312 | for(int i=0; i= crit_threshold)) { 336 | crit++; 337 | } else if(warn_threshold && (attribute.getRaw() >= warn_threshold)) { 338 | warn++; 339 | } 340 | 341 | // Accumulate the performance data 342 | perfdata << " " << attribute << ";"; 343 | if(warn_threshold) 344 | perfdata << warn_threshold; 345 | perfdata << ";"; 346 | if(crit_threshold) 347 | perfdata << crit_threshold; 348 | perfdata << ";;"; 349 | 350 | } 351 | 352 | // Determine the state to report 353 | if(advisory || warn) 354 | code = max(code, NAGIOS_WARNING); 355 | 356 | if(prdfail || crit) 357 | code = max(code, NAGIOS_CRITICAL); 358 | 359 | } 360 | 361 | /* 362 | * Function: check_smart_log 363 | * ------------------------- 364 | * Checks for the existence of SMART logs 365 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 366 | * fd: File descriptor pointing at a SCSI or SCSI generic device node 367 | * code: Reference to the current return code 368 | * logs: Reference to a count of the number of SMART logs 369 | */ 370 | void check_smart_log(int fd, int& code, int& logs) { 371 | 372 | // Read the SMART log directory 373 | smart_log_directory log_directory; 374 | ata_smart_read_log_directory(fd, reinterpret_cast(&log_directory)); 375 | 376 | // Calculate the number of SMART log sectors to read and allocate a buffer 377 | uint16_t smart_log_sectors = StorageEndian::swap(log_directory.data_blocks[ATA_LOG_ADDRESS_SMART]); 378 | if(!smart_log_sectors) 379 | return; 380 | 381 | smart_log_summary* summaries = new smart_log_summary[smart_log_sectors]; 382 | 383 | // Read the logs in 384 | ata_smart_read_log(fd, reinterpret_cast(summaries), ATA_LOG_ADDRESS_SMART, smart_log_sectors); 385 | 386 | // Check for any logged errors 387 | for(int i=0; i tokens; 415 | string token1, token2; 416 | 417 | // Split the input into key value pairs 418 | vector key_value_pairs; 419 | while(getline(in_stream, token1, ',')) { 420 | tokens.push_back(token1); 421 | } 422 | 423 | // Split each key value pair 424 | for(vector::iterator i = tokens.begin(); i != tokens.end(); i++) { 425 | 426 | istringstream tok_stream(*i); 427 | 428 | // Read the first token delimited by = 429 | getline(tok_stream, token1, ':'); 430 | if(!tok_stream.good()) { 431 | return false; 432 | } 433 | 434 | // Read the second token, which shoud result in EOF 435 | getline(tok_stream, token2); 436 | if(!tok_stream.eof()) { 437 | return false; 438 | } 439 | 440 | // Parse the tokens and ensure they are integers 441 | char* p1; 442 | char* p2; 443 | 444 | unsigned long k = strtol(token1.c_str(), &p1, 10); 445 | unsigned long v = strtol(token2.c_str(), &p2, 10); 446 | 447 | if(*p1 || *p2) { 448 | return false; 449 | } 450 | 451 | thresholds[k] = v; 452 | 453 | } 454 | 455 | return true; 456 | } 457 | 458 | /* 459 | * Function: main 460 | * -------------- 461 | * Reads device identity and checks for SMART capability, if so reads 462 | * the SMART data and thresholds and checks for any predictive failures 463 | */ 464 | int main(int argc, char** argv) { 465 | 466 | const char* device = 0; 467 | const char* warning = ""; 468 | const char* critical = ""; 469 | 470 | static struct option long_options[] = { 471 | { "help", no_argument, 0, 'h' }, 472 | { "version", no_argument, 0, 'V' }, 473 | { "device", required_argument, 0, 'd' }, 474 | { "warning", required_argument, 0, 'w' }, 475 | { "critical", required_argument, 0, 'c' }, 476 | { 0, 0, 0, 0 } 477 | }; 478 | 479 | int c; 480 | while((c = getopt_long(argc, argv, "hVd:w:c:", long_options, 0)) != -1) { 481 | switch(c) { 482 | case 'h': 483 | help(); 484 | exit(0); 485 | case 'V': 486 | version(); 487 | exit(0); 488 | case 'd': 489 | device = optarg; 490 | break; 491 | case 'w': 492 | warning = optarg; 493 | break; 494 | case 'c': 495 | critical = optarg; 496 | break; 497 | default: 498 | usage(); 499 | exit(1); 500 | break; 501 | } 502 | } 503 | 504 | // Check for required arguments 505 | if(!device) { 506 | help(); 507 | exit(NAGIOS_UNKNOWN); 508 | } 509 | 510 | // Parse optional arguments 511 | SmartThresholdMap warning_thresholds; 512 | if(!parse_thresholds(warning_thresholds, warning)) { 513 | help(); 514 | exit(NAGIOS_UNKNOWN); 515 | } 516 | 517 | SmartThresholdMap critical_thresholds; 518 | if(!parse_thresholds(critical_thresholds, critical)) { 519 | help(); 520 | exit(NAGIOS_UNKNOWN); 521 | } 522 | 523 | // Check the device is compatible with the check 524 | int fd = open(device, O_RDWR); 525 | if(fd == -1) { 526 | cerr << "UNKNOWN: unable to open device " << device << endl; 527 | exit(NAGIOS_UNKNOWN); 528 | } 529 | 530 | int sg_version; 531 | if((ioctl(fd, SG_GET_VERSION_NUM, &sg_version) == -1) || sg_version < 30000) { 532 | cerr << "UNKNOWN: " << device << " is either not an sg device, or the driver is old" << endl; 533 | exit(NAGIOS_UNKNOWN); 534 | } 535 | 536 | // Check the device can use SMART and that it is enabled 537 | uint16_t identify[SECTOR_SIZE / 2]; 538 | if(!ata_identify(fd, reinterpret_cast(identify))) { 539 | cout << "OK: ATA command set unsupported" << endl; 540 | exit(NAGIOS_OK); 541 | } 542 | 543 | if(~StorageEndian::swap(identify[82]) & 0x01) { 544 | cout << "OK: SMART feature set unsupported" << endl; 545 | exit(NAGIOS_OK); 546 | } 547 | 548 | if(~StorageEndian::swap(identify[85]) & 0x01) { 549 | cout << "UNKNOWN: SMART feature set disabled" << endl; 550 | exit(NAGIOS_UNKNOWN); 551 | } 552 | 553 | int code = NAGIOS_OK; 554 | int prdfail = 0; 555 | int advisory = 0; 556 | int crit = 0; 557 | int warn = 0; 558 | int logs = 0; 559 | stringstream perfdata; 560 | 561 | // Perform the checks 562 | check_smart_attributes(fd, critical_thresholds, warning_thresholds, code, prdfail, advisory, crit, warn, perfdata); 563 | check_smart_log(fd, code, logs); 564 | 565 | // Print out the results and performance data 566 | const char* status[] = { "OK", "WARNING", "CRITICAL" }; 567 | cout << status[code] 568 | << ": prdfail " << prdfail 569 | << ", advisory " << advisory 570 | << ", critical " << crit 571 | << ", warning " << warn 572 | << ", logs " << logs 573 | << " |" << perfdata.str() 574 | << endl; 575 | 576 | close(fd); 577 | 578 | return code; 579 | 580 | } 581 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | nagios-plugin-check-scsi-smart (1.2.3) UNRELEASED; urgency=medium 2 | 3 | * Version 1.2.3 4 | 5 | -- Simon Murray Thu, 2 Feb 2017 13:37:00 +0000 6 | 7 | nagios-plugin-check-scsi-smart (1.2.2) UNRELEASED; urgency=medium 8 | 9 | * Version 1.2.2 10 | 11 | -- Simon Murray Thu, 10 Mar 2016 12:31:00 +0000 12 | 13 | nagios-plugin-check-scsi-smart (1.2.1) UNRELEASED; urgency=medium 14 | 15 | * Version 1.2.1 16 | 17 | -- Simon Murray Thu, 10 Mar 2016 09:57:00 +0000 18 | 19 | nagios-plugin-check-scsi-smart (1.2.0) UNRELEASED; urgency=medium 20 | 21 | * Version 1.2.0 22 | 23 | -- Simon Murray Wed, 9 Mar 2016 12:42:00 +0000 24 | 25 | nagios-plugin-check-scsi-smart (1.1.1) UNRELEASED; urgency=medium 26 | 27 | * Version 1.1.1 28 | 29 | -- Simon Murray Wed, 9 Mar 2016 09:51:00 +0000 30 | 31 | nagios-plugin-check-scsi-smart (1.1.0) UNRELEASED; urgency=medium 32 | 33 | * Version 1.1.0 34 | 35 | -- Simon Murray Mon, 7 Mar 2016 16:19:00 +0000 36 | 37 | nagios-plugin-check-scsi-smart (1.0.3) UNRELEASED; urgency=medium 38 | 39 | * Version 1.0.3 40 | 41 | -- Simon Murray Thu, 7 Jan 2016 13:54:00 +0000 42 | 43 | nagios-plugin-check-scsi-smart (1.0.2) UNRELEASED; urgency=medium 44 | 45 | * Version 1.0.2 46 | 47 | -- Simon Murray Thu, 7 Jan 2016 13:41:00 +0000 48 | 49 | nagios-plugin-check-scsi-smart (1.0.1) UNRELEASED; urgency=medium 50 | 51 | * Version 1.0.1 52 | 53 | -- Simon Murray Thu, 7 Jan 2016 13:24:00 +0000 54 | 55 | nagios-plugin-check-scsi-smart (1.0.0) UNRELEASED; urgency=medium 56 | 57 | * Initial release. 58 | 59 | -- Simon Murray Tue, 22 Dec 2015 11:48:16 +0000 60 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: nagios-plugin-check-scsi-smart 2 | Maintainer: Simon Murray 3 | Section: misc 4 | Priority: optional 5 | Standards-Version: 3.9.6 6 | Build-Depends: debhelper (>=9) 7 | 8 | Package: nagios-plugin-check-scsi-smart 9 | Architecture: any 10 | Depends: ${shlibs:Depends}, ${misc:Depends} 11 | Description: Nagios SMART disk check over SCSI 12 | Tunnels ATA SMART commands over SCSI transports. This allows ATA SMART checks 13 | to work for SATA drives which are directly attached to a SATA controller a SAS 14 | HBA or a SAS expander. The SAT translation layer handles decpasulating the 15 | ATA command at the relvant boundary between SCSI and SATA protocols. 16 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 2 | Name: nagios-plugin-check-scsi-smart 3 | Maintainer: Simon Murray 4 | Source: http://github.com/spjmurray/check_scsi_smart 5 | 6 | Copyright: 2015 Simon Murray 7 | License: GPL-3 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | . 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | . 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | . 21 | On Debian systems, the full text of the GPU General Public 22 | License version 3 can be found in the file 23 | `/usr/share/common-licenses/GPL-3'. 24 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | %: 3 | dh $@ 4 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /endian.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SMART Nagios/Icinga Disk Check 3 | * ------------------------------ 4 | * 5 | * License 6 | * ------- 7 | * (C) 2015-2017 Simon Murray 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | #ifndef _endian_H_ 24 | #define _endian_H_ 25 | 26 | #include 27 | 28 | /** 29 | * Class: StorageEndian 30 | * -------------------- 31 | * Provides endian abstraction for loads and stores from ATA/SCSI operations. 32 | * These unsurprisingly are little-endian and need byteswapping on big endian 33 | * architectures. 34 | */ 35 | class StorageEndian { 36 | public: 37 | template 38 | static inline T swap(T t) { 39 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 40 | return t; 41 | #else 42 | switch(sizeof(T)) { 43 | case 1: 44 | return t; 45 | case 2: 46 | return bswap_16(t); 47 | case 4: 48 | return bswap_32(t); 49 | case 16: 50 | return bswap_64(t); 51 | } 52 | #endif 53 | } 54 | }; 55 | 56 | #endif//_endian_H_ 57 | -------------------------------------------------------------------------------- /nagios-plugin-check-scsi-smart.spec: -------------------------------------------------------------------------------- 1 | Name: nagios-plugin-check-scsi-smart 2 | Version: 1.2.3 3 | Release: 1 4 | License: GPLv3 5 | Summary: Nagios plugin to perform SMART checks on SATA devices on SCSI buses 6 | Group: System Environment/Base 7 | URL: http://github.com/spjmurray/check_scsi_smart 8 | 9 | %description 10 | Tunnels ATA SMART commands over SCSI transports. This allows ATA SMART checks 11 | to work for SATA drives which are directly attached to a SATA controller a SAS 12 | HBA or a SAS expander. The SAT translation layer handles decpasulating the 13 | ATA command at the relvant boundary between SCSI and SATA protocols. 14 | 15 | %build 16 | make 17 | 18 | %install 19 | make install LIBDIR=lib64 DESTDIR=%{buildroot} 20 | 21 | %files 22 | /usr/lib64/nagios/plugins/check_scsi_smart 23 | -------------------------------------------------------------------------------- /scsi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SMART Nagios/Icinga Disk Check 3 | * ------------------------------ 4 | * 5 | * License 6 | * ------- 7 | * (C) 2015-2017 Simon Murray 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | #ifndef _scsi_H_ 24 | #define _scsi_H_ 25 | 26 | #include 27 | 28 | /* SCSI primary commands */ 29 | #define SBC_ATA_PASS_THROUGH 0x85 30 | 31 | /* 32 | * Struct: sbc_ata_pass_through 33 | * ---------------------------- 34 | * SCSI CDB for tunneling ATA commands over the SCSI command protocol 35 | * to a SAT which then translates to a native ATA command to the actual 36 | * device. May be handled by Linux for directly attached devices or 37 | * via a SAS HBA/expander. 38 | */ 39 | typedef struct { 40 | uint8_t operation_code; 41 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 42 | uint8_t extend: 1; 43 | uint8_t protocol: 4; 44 | uint8_t multiple_count: 3; 45 | #else 46 | uint8_t multiple_count: 3; 47 | uint8_t protocol: 4; 48 | uint8_t extend: 1; 49 | #endif 50 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 51 | uint8_t t_length: 2; 52 | uint8_t byte_block: 1; 53 | uint8_t t_dir: 1; 54 | uint8_t t_type: 1; 55 | uint8_t ck_cond: 1; 56 | uint8_t off_line: 2; 57 | #else 58 | uint8_t off_line: 2; 59 | uint8_t ck_cond: 1; 60 | uint8_t t_type: 1; 61 | uint8_t t_dir: 1; 62 | uint8_t byte_block: 1; 63 | uint8_t t_length: 2; 64 | #endif 65 | uint8_t features_15_8; 66 | uint8_t features_7_0; 67 | uint8_t count_15_8; 68 | uint8_t count_7_0; 69 | uint8_t lba_31_24; 70 | uint8_t lba_7_0; 71 | uint8_t lba_39_32; 72 | uint8_t lba_15_8; 73 | uint8_t lba_47_40; 74 | uint8_t lba_23_16; 75 | uint8_t device; 76 | uint8_t command; 77 | uint8_t control; 78 | } sbc_ata_pass_through; 79 | 80 | #endif//_scsi_H_ 81 | -------------------------------------------------------------------------------- /smart.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * SMART Nagios/Icinga Disk Check 3 | * ------------------------------ 4 | * 5 | * License 6 | * ------- 7 | * (C) 2015-2017 Simon Murray 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | #include "endian.h" 24 | #include "smart.h" 25 | 26 | /** 27 | * Function: SmartAttribute::SmartAttribute(const smart_attribute&) 28 | * ---------------------------------------------------------------- 29 | * Class constructor to munge raw data structures into a sensible format 30 | * attribute: Reference to a smart_attribute structure 31 | */ 32 | SmartAttribute::SmartAttribute(const smart_attribute& attribute) 33 | : id(StorageEndian::swap(attribute.id)), 34 | pre_fail(StorageEndian::swap(attribute.flags) & 0x1), 35 | offline(StorageEndian::swap(attribute.flags) & 0x2), 36 | value(StorageEndian::swap(attribute.value)), 37 | raw((static_cast(StorageEndian::swap(attribute.raw_hi)) << 32) | 38 | static_cast(StorageEndian::swap(attribute.raw_lo))) { 39 | 40 | // Logic shamelessly lifted from smartmontools 41 | switch(id) { 42 | case 3: // Spin up time 43 | case 5: // Reallocated sector count 44 | case 196: // Reallocated event count 45 | raw &= 0xffff; 46 | break; 47 | case 9: // Power on hours 48 | case 240: // Head flying hours 49 | raw &= 0xffffff; 50 | break; 51 | case 190: // Temperature 52 | case 194: // Temperature 53 | raw &= 0xff; 54 | break; 55 | default: 56 | break; 57 | } 58 | 59 | } 60 | 61 | /** 62 | * Function: SmartAttribute::operator<=(const SmartThreshold&) 63 | * ----------------------------------------------------------- 64 | * Compares a value to a threshold 65 | * threshold: Reference to a SmartThreshold object to check against 66 | */ 67 | bool SmartAttribute::operator<=(const SmartThreshold& threshold) const { 68 | 69 | return value <= threshold.getThreshold(); 70 | 71 | } 72 | 73 | /** 74 | * Function: operator<<(ostream&, const SmartAttribute&) 75 | * ---------------------------------------- 76 | * Function to dump human readable text to an output stream 77 | * o: Class implmenting std::ostream 78 | * id: Reference to a SmartAttribute class 79 | */ 80 | ostream& operator<<(ostream& o, const SmartAttribute& attribute) { 81 | 82 | static const char* labels[] = { 83 | // 0x00 84 | "unknown", 85 | "read_error_rate", 86 | "throughput_performance", 87 | "spin_up_time", 88 | "start_stop_count", 89 | "reallocated_sectors_count", 90 | "read_channel_margin", 91 | "seek_error_rate", 92 | "seek_time_performance", 93 | "power_on_hours", 94 | "spin_retry_count", 95 | "recalibration_retries", 96 | "power_cycle_count", 97 | "soft_read_error_rate", 98 | "unknown", 99 | "unknown", 100 | // 0x10 101 | "unknown", 102 | "unknown", 103 | "unknown", 104 | "unknown", 105 | "unknown", 106 | "unknown", 107 | "unknown", 108 | "unknown", 109 | "current_helium_level", 110 | "unknown", 111 | "unknown", 112 | "unknown", 113 | "unknown", 114 | "unknown", 115 | "unknown", 116 | "unknown", 117 | // 0x20 118 | "unknown", 119 | "unknown", 120 | "unknown", 121 | "unknown", 122 | "unknown", 123 | "unknown", 124 | "unknown", 125 | "unknown", 126 | "unknown", 127 | "unknown", 128 | "unknown", 129 | "unknown", 130 | "unknown", 131 | "unknown", 132 | "unknown", 133 | "unknown", 134 | // 0x30 135 | "unknown", 136 | "unknown", 137 | "unknown", 138 | "unknown", 139 | "unknown", 140 | "unknown", 141 | "unknown", 142 | "unknown", 143 | "unknown", 144 | "unknown", 145 | "unknown", 146 | "unknown", 147 | "unknown", 148 | "unknown", 149 | "unknown", 150 | "unknown", 151 | // 0x40 152 | "unknown", 153 | "unknown", 154 | "unknown", 155 | "unknown", 156 | "unknown", 157 | "unknown", 158 | "unknown", 159 | "unknown", 160 | "unknown", 161 | "unknown", 162 | "unknown", 163 | "unknown", 164 | "unknown", 165 | "unknown", 166 | "unknown", 167 | "unknown", 168 | // 0x50 169 | "unknown", 170 | "unknown", 171 | "unknown", 172 | "unknown", 173 | "unknown", 174 | "unknown", 175 | "unknown", 176 | "unknown", 177 | "unknown", 178 | "unknown", 179 | "unknown", 180 | "unknown", 181 | "unknown", 182 | "unknown", 183 | "unknown", 184 | "unknown", 185 | // 0x60 186 | "unknown", 187 | "unknown", 188 | "unknown", 189 | "unknown", 190 | "unknown", 191 | "unknown", 192 | "unknown", 193 | "unknown", 194 | "unknown", 195 | "unknown", 196 | "unknown", 197 | "unknown", 198 | "unknown", 199 | "unknown", 200 | "unknown", 201 | "unknown", 202 | // 0x70 203 | "unknown", 204 | "unknown", 205 | "unknown", 206 | "unknown", 207 | "unknown", 208 | "unknown", 209 | "unknown", 210 | "unknown", 211 | "unknown", 212 | "unknown", 213 | "unknown", 214 | "unknown", 215 | "unknown", 216 | "unknown", 217 | "unknown", 218 | "unknown", 219 | // 0x80 220 | "unknown", 221 | "unknown", 222 | "unknown", 223 | "unknown", 224 | "unknown", 225 | "unknown", 226 | "unknown", 227 | "unknown", 228 | "unknown", 229 | "unknown", 230 | "unknown", 231 | "unknown", 232 | "unknown", 233 | "unknown", 234 | "unknown", 235 | "unknown", 236 | // 0x90 237 | "unknown", 238 | "unknown", 239 | "unknown", 240 | "unknown", 241 | "unknown", 242 | "unknown", 243 | "unknown", 244 | "unknown", 245 | "unknown", 246 | "unknown", 247 | "unknown", 248 | "unknown", 249 | "unknown", 250 | "unknown", 251 | "unknown", 252 | "unknown", 253 | // 0xa0 254 | "unknown", 255 | "unknown", 256 | "unknown", 257 | "unknown", 258 | "unknown", 259 | "unknown", 260 | "unknown", 261 | "unknown", 262 | "unknown", 263 | "unknown", 264 | "available_reserved_space", 265 | "ssd_program_fail_count", 266 | "ssd_erase_fail_count", 267 | "ssd_wear_leveling_count", 268 | "unexpected_power_loss_count", 269 | "power_loss_protection_failure", 270 | // 0xb0 271 | "erase_fail_count", 272 | "wear_range_delta", 273 | "unknown", 274 | "used_reserved_block_count_total", 275 | "unused_reserved_block_count_total", 276 | "program_fail_count_total", 277 | "erase_fail_count", 278 | "sata_downshift_error_count", 279 | "end_to_end_error", 280 | "head_stability", 281 | "induced_op_vibration_detection", 282 | "reported_uncorrectable_errors", 283 | "command_timeout", 284 | "high_fly_writes", 285 | "airflow_temperature", 286 | "g_sense_error_rate", 287 | // 0xc0 288 | "power_off_retract_count", 289 | "load_cycle_count", 290 | "temperature", 291 | "hardware_ecc_recovered", 292 | "reallocation_event_count", 293 | "current_pending_sector_count", 294 | "uncorrectable_sector_count", 295 | "ultradma_crc_error_count", 296 | "multi_zone_error_rate", 297 | "soft_read_error_rate", 298 | "data_address_mark_errors", 299 | "run_out_cancel", 300 | "soft_ecc_correction", 301 | "thermal_asperity_rate", 302 | "flying_height", 303 | "spin_height_current", 304 | // 0xd0 305 | "spin_buzz", 306 | "offline_seek_performance", 307 | "vibration_during_write", 308 | "vibration_during_write", 309 | "shock_during_write", 310 | "unknown", 311 | "unknown", 312 | "unknown", 313 | "unknown", 314 | "unknown", 315 | "unknown", 316 | "unknown", 317 | "disk_shift", 318 | "g_sense_error_rate", 319 | "loaded_hours", 320 | "load_unload_retry_count", 321 | // 0xe0 322 | "load_friction", 323 | "load_unload_cycle_count", 324 | "load_in_time", 325 | "torque_amplification_count", 326 | "power_off_retract_cycle", 327 | "unknown", 328 | "drive_life_protection_status", 329 | "temperature", 330 | "available_reserved_space", 331 | "media_wearout_indicator", 332 | "average_erase_count", 333 | "good_block_count", 334 | "unknown", 335 | "unknown", 336 | "unknown", 337 | "unknown", 338 | // 0xf0 339 | "flying_head_hours", 340 | "total_lbas_written", 341 | "total_lbas_read", 342 | "total_lbas_written_expanded", 343 | "total_lbas_read_expanded", 344 | "unknown", 345 | "unknown", 346 | "unknown", 347 | "unknown", 348 | "nand_writes_1gib", 349 | "read_error_retry_rate", 350 | "minimum_spares_remaining", 351 | "newly_added_bad_flash_block", 352 | "unknown", 353 | "free_fall_protection", 354 | "unknown", 355 | }; 356 | 357 | o << dec << static_cast(attribute.id) << "_" << labels[attribute.id] << "=" << attribute.raw; 358 | 359 | return o; 360 | 361 | } 362 | 363 | /** 364 | * Function: SmartThreshold::SmartThreshold(const smart_threshold&) 365 | * ---------------------------------------------------------------- 366 | * Class constructor to create a SmartThreshold object from raw data 367 | * threshold: Reference to a smart_threshold object 368 | */ 369 | SmartThreshold::SmartThreshold(const smart_threshold& threshold) 370 | : threshold(StorageEndian::swap(threshold.threshold)) 371 | {} 372 | -------------------------------------------------------------------------------- /smart.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SMART Nagios/Icinga Disk Check 3 | * ------------------------------ 4 | * 5 | * License 6 | * ------- 7 | * (C) 2015-2017 Simon Murray 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | */ 22 | 23 | #ifndef _smart_H_ 24 | #define _smart_H_ 25 | 26 | #include 27 | #include 28 | 29 | using namespace std; 30 | 31 | /* Class Declarations */ 32 | class SmartAttribute; 33 | class SmartThreshold; 34 | 35 | /* SMART functions */ 36 | const uint8_t SMART_READ_DATA = 0xd0; 37 | const uint8_t SMART_READ_THRESHOLDS = 0xd1; 38 | const uint8_t SMART_READ_LOG = 0xd5; 39 | const uint8_t SMART_RETURN_STATUS = 0xda; 40 | 41 | /* SMART off-line status */ 42 | const uint8_t SMART_OFF_LINE_STATUS_NEVER_STARTED = 0x00; 43 | const uint8_t SMART_OFF_LINE_STATUS_COMPLETED = 0x02; 44 | const uint8_t SMART_OFF_LINE_STATUS_IN_PROGRESS = 0x03; 45 | const uint8_t SMART_OFF_LINE_STATUS_SUSPENDED = 0x04; 46 | const uint8_t SMART_OFF_LINE_STATUS_ABORTED_HOST = 0x05; 47 | const uint8_t SMART_OFF_LINE_STATUS_ABORTED_DEVICE = 0x06; 48 | 49 | /* Attributes in a smart_data page */ 50 | const uint8_t SMART_ATTRIBUTE_NUM = 30; 51 | 52 | /* 53 | * Struct: smart_attribute 54 | * ----------------------- 55 | * Vendor specific SMART attribute as returned by a SMART READ DATA ATA 56 | * command. 57 | */ 58 | typedef struct __attribute__((packed)) { 59 | uint8_t id; 60 | uint16_t flags; 61 | uint8_t value; 62 | uint8_t worst; 63 | uint32_t raw_lo; 64 | uint16_t raw_hi; 65 | uint8_t pad; 66 | } smart_attribute; 67 | 68 | /* 69 | * Struct: smart_data 70 | * ------------------ 71 | * Standardized ATA SMART data returned by SMART READ DATA 72 | */ 73 | typedef struct __attribute__((packed)) { 74 | uint16_t version; 75 | smart_attribute attributes[SMART_ATTRIBUTE_NUM]; 76 | uint8_t offline_data_collection_status; 77 | uint8_t self_test_execution_status; 78 | uint16_t offline_collection_time; 79 | uint8_t vendor_specific1; 80 | uint8_t offline_collection_capability; 81 | uint16_t smart_capability; 82 | uint8_t error_logging_capbility; 83 | uint8_t vendor_specific2; 84 | uint8_t short_self_test_polling_time; 85 | uint8_t extended_self_test_polling_time; 86 | uint8_t conveyance_self_test_polling_time; 87 | uint16_t extended_self_test_routine_polling_time; 88 | uint8_t reserved[9]; 89 | uint8_t vendor_specific3[125]; 90 | uint8_t checksum; 91 | } smart_data; 92 | 93 | /* 94 | * Struct: smart_threshold 95 | * ----------------------- 96 | * Vendor specific SMART threshold as returned by a SMART READ THRESHOLDS 97 | * ATA command. This is now obsolete, and should be rolled up by the device 98 | * into and LBA field which can be attained via the SMART RETURN STATUS 99 | * command 100 | */ 101 | typedef struct __attribute__((packed)) { 102 | uint8_t id; 103 | uint8_t threshold; 104 | uint8_t pad[10]; 105 | } smart_threshold; 106 | 107 | /* 108 | * Struct: smart_thresholds 109 | * ------------------------ 110 | * Standardized ATA SMART threshold data returned by SMART READ THRESHOLDS 111 | */ 112 | typedef struct __attribute__((packed)) { 113 | uint16_t version; 114 | smart_threshold thresholds[SMART_ATTRIBUTE_NUM]; 115 | uint8_t reserved[149]; 116 | uint8_t checksum; 117 | } smart_thresholds; 118 | 119 | /* 120 | * Struct: smart_log_directory 121 | * ------------------------- 122 | * Structure defining the directory version and number of logs available 123 | * for each address. Index 0 is the version, this is kept as an array to 124 | * enable resue of ATA_LOG_ADDRESS_* macros. 125 | */ 126 | typedef struct { 127 | uint16_t data_blocks[256]; 128 | } smart_log_directory; 129 | 130 | /* 131 | * Struct: smart_log_command 132 | * ------------------------- 133 | * SMART log command 134 | */ 135 | typedef struct __attribute__((packed)) { 136 | uint8_t command; 137 | uint8_t feature; 138 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 139 | uint32_t lba: 24; 140 | uint32_t count: 8; 141 | #else 142 | uint32_t count: 8; 143 | uint32_t lba: 24; 144 | #endif 145 | uint8_t device; 146 | uint8_t init; 147 | uint32_t timestamp; 148 | } smart_log_command; 149 | 150 | /* 151 | * Struct: smart_log_error 152 | * ----------------------- 153 | * SMART log error structure defining LBA, device, status, timestamp etc. 154 | */ 155 | typedef struct __attribute__((packed)) { 156 | uint8_t reserved; 157 | uint8_t error; 158 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 159 | uint32_t lba: 24; 160 | uint32_t count: 8; 161 | #else 162 | uint32_t count: 8; 163 | uint32_t lba: 24; 164 | #endif 165 | uint8_t device; 166 | uint8_t status; 167 | uint8_t extended[19]; 168 | uint8_t state; 169 | uint16_t timestamp; 170 | } smart_log_error; 171 | 172 | /* 173 | * Struct: smart_log_data 174 | * ---------------------- 175 | * Sructure to hold an error and the preceding commands leading up to it 176 | */ 177 | typedef struct __attribute__((packed)) { 178 | smart_log_command command[5]; 179 | smart_log_error error; 180 | } smart_log_data; 181 | 182 | /* 183 | * Struct: smart_log_summary 184 | * ------------------------- 185 | * Top level log summary containing upto 5 errors 186 | */ 187 | typedef struct __attribute__((packed)) { 188 | uint8_t version; 189 | uint8_t index; 190 | smart_log_data data[5]; 191 | uint16_t count; 192 | uint8_t reserved[57]; 193 | uint8_t checksum; 194 | } smart_log_summary; 195 | 196 | /* 197 | * Class: SmartAttribute 198 | * --------------- 199 | * Wraps up the smart attribute and formatting 200 | */ 201 | class SmartAttribute { 202 | 203 | public: 204 | /** 205 | * Function: SmartAttribute::SmartAttribute(const smart_attribute&) 206 | * ---------------------------------------------------------------- 207 | * Class constructor to munge raw data structures into a sensible format 208 | * attribute: Reference to a smart_attribute structure 209 | */ 210 | SmartAttribute(const smart_attribute& attribute); 211 | 212 | /** 213 | * Function SmartAttribute::getID() 214 | * -------------------------------- 215 | * Returns the SMART attribute ID 216 | */ 217 | inline uint8_t getID() const { 218 | return id; 219 | } 220 | 221 | /** 222 | * Function SmartAttribute::getPreFail() 223 | * ------------------------------------- 224 | * Return whether this attribute predicts failure within 24h 225 | */ 226 | inline bool getPreFail() const { 227 | return pre_fail; 228 | } 229 | 230 | /** 231 | * Function: SmartAttribute:getRaw() 232 | * --------------------------------- 233 | * Return the sanitised raw value 234 | */ 235 | inline uint64_t getRaw() const { 236 | return raw; 237 | } 238 | 239 | /** 240 | * Function: SmartAttribute::idValid() 241 | * ----------------------------------- 242 | * Checks whether a SMART ID is valid 243 | */ 244 | inline bool idValid() const { 245 | return id != 0; 246 | } 247 | 248 | /** 249 | * Function: SmartAttribute::valueValid() 250 | * -------------------------------------- 251 | * Checks whether a SMART value is within the valid limits 252 | */ 253 | inline bool valueValid() const { 254 | return value > 0x0 && value < 0xfe; 255 | } 256 | 257 | /** 258 | * Function: SmartAttribute::operator<=(const SmartThreshold&) 259 | * ----------------------------------------------------------- 260 | * Compares a value to a threshold 261 | * threshold: Reference to a SmartThreshold object to check against 262 | */ 263 | bool operator<=(const SmartThreshold& threshold) const; 264 | 265 | friend ostream& operator<<(ostream& o, const SmartAttribute& id); 266 | 267 | private: 268 | uint8_t id; 269 | bool pre_fail; 270 | bool offline; 271 | uint8_t value; 272 | uint64_t raw; 273 | 274 | }; 275 | 276 | /** 277 | * Function: operator<<(ostream&, const SmartAttribute&) 278 | * ---------------------------------------- 279 | * Function to dump human readable text to an output stream 280 | * o: Class implmenting std::ostream 281 | * id: Reference to a SmartAttribute class 282 | */ 283 | ostream& operator<<(ostream& o, const SmartAttribute& attribute); 284 | 285 | /** 286 | * Class: SmartThreshold 287 | * --------------------- 288 | * Wrapper for a SMART threshold 289 | */ 290 | class SmartThreshold { 291 | 292 | public: 293 | 294 | /** 295 | * Function: SmartThreshold::SmartThreshold(const smart_threshold&) 296 | * ---------------------------------------------------------------- 297 | * Class constructor to create a SmartThreshold object from raw data 298 | * threshold: Reference to a smart_threshold object 299 | */ 300 | SmartThreshold(const smart_threshold& threshold); 301 | 302 | /** 303 | * Function: SmartThreshold::getThreshold() 304 | * ---------------------------------------- 305 | * Accessor method for the threshold value 306 | */ 307 | inline uint8_t getThreshold() const { 308 | return threshold; 309 | } 310 | 311 | private: 312 | uint8_t threshold; 313 | 314 | }; 315 | 316 | #endif//_smart_H_ 317 | --------------------------------------------------------------------------------