Merge pull request #2045 from RincewindsHat/fix/calloc_argument_order
[monitoring-plugins.git] / plugins / check_ide_smart.c
blob9640ef70018ae3bae520f6fa8600432a7fe138d0
1 /*****************************************************************************
3 * Monitoring check_ide_smart plugin
4 * ide-smart 1.3 - IDE S.M.A.R.T. checking tool
6 * License: GPL
7 * Copyright (C) 1998-1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>
8 * 1998 Gadi Oxman <gadio@netvision.net.il>
9 * Copyright (c) 2000 Robert Dale <rdale@digital-mission.com>
10 * Copyright (c) 2000-2024 Monitoring Plugins Development Team
12 * Description:
14 * This file contains the check_ide_smart plugin
16 * This plugin checks a local hard drive with the (Linux specific) SMART
17 * interface
20 * This program is free software: you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation, either version 3 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34 *****************************************************************************/
36 const char *progname = "check_ide_smart";
37 const char *copyright = "1998-2024";
38 const char *email = "devel@monitoring-plugins.org";
40 #include "common.h"
41 #include "utils.h"
43 static void print_help(void);
44 void print_usage(void);
46 #include <sys/stat.h>
47 #include <sys/ioctl.h>
48 #include <fcntl.h>
49 #ifdef __linux__
50 # include <linux/hdreg.h>
51 # include <linux/types.h>
53 # define OPEN_MODE O_RDONLY
54 #endif /* __linux__ */
55 #ifdef __NetBSD__
56 # include <sys/device.h>
57 # include <sys/param.h>
58 # include <sys/sysctl.h>
59 # include <sys/videoio.h> /* for __u8 and friends */
60 # include <sys/scsiio.h>
61 # include <sys/ataio.h>
62 # include <dev/ata/atareg.h>
63 # include <dev/ic/wdcreg.h>
65 # define SMART_ENABLE WDSM_ENABLE_OPS
66 # define SMART_DISABLE WDSM_DISABLE_OPS
67 # define SMART_IMMEDIATE_OFFLINE WDSM_EXEC_OFFL_IMM
68 # define SMART_AUTO_OFFLINE 0xdb /* undefined in NetBSD headers */
70 # define OPEN_MODE O_RDWR
71 #endif /* __NetBSD__ */
72 #include <errno.h>
74 #define NR_ATTRIBUTES 30
76 #define PREFAILURE 2
77 #define ADVISORY 1
78 #define OPERATIONAL 0
79 #define UNKNOWN -1
81 typedef struct threshold_s {
82 __u8 id;
83 __u8 threshold;
84 __u8 reserved[10];
85 } __attribute__((packed)) threshold_t;
87 typedef struct thresholds_s {
88 __u16 revision;
89 threshold_t thresholds[NR_ATTRIBUTES];
90 __u8 reserved[18];
91 __u8 vendor[131];
92 __u8 checksum;
93 } __attribute__((packed)) thresholds_t;
95 typedef struct value_s {
96 __u8 id;
97 __u16 status;
98 __u8 value;
99 __u8 vendor[8];
100 } __attribute__((packed)) value_t;
102 typedef struct values_s {
103 __u16 revision;
104 value_t values[NR_ATTRIBUTES];
105 __u8 offline_status;
106 __u8 vendor1;
107 __u16 offline_timeout;
108 __u8 vendor2;
109 __u8 offline_capability;
110 __u16 smart_capability;
111 __u8 reserved[16];
112 __u8 vendor[125];
113 __u8 checksum;
114 } __attribute__((packed)) values_t;
116 static struct {
117 __u8 value;
118 char *text;
119 } offline_status_text[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"},
120 {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
122 static struct {
123 __u8 value;
124 char *text;
125 } smart_command[] = {{SMART_ENABLE, "SMART_ENABLE"},
126 {SMART_DISABLE, "SMART_DISABLE"},
127 {SMART_IMMEDIATE_OFFLINE, "SMART_IMMEDIATE_OFFLINE"},
128 {SMART_AUTO_OFFLINE, "SMART_AUTO_OFFLINE"}};
130 /* Index to smart_command table, keep in order */
131 enum SmartCommand {
132 SMART_CMD_ENABLE,
133 SMART_CMD_DISABLE,
134 SMART_CMD_IMMEDIATE_OFFLINE,
135 SMART_CMD_AUTO_OFFLINE
138 static char *get_offline_text(int);
139 static int smart_read_values(int, values_t *);
140 static int nagios(values_t *, thresholds_t *);
141 static void print_value(value_t *, threshold_t *);
142 static void print_values(values_t *, thresholds_t *);
143 static int smart_cmd_simple(int, enum SmartCommand, __u8, bool);
144 static int smart_read_thresholds(int, thresholds_t *);
145 static bool verbose = false;
147 int main(int argc, char *argv[]) {
148 char *device = NULL;
149 int o;
150 int longindex;
151 int retval = 0;
153 thresholds_t thresholds;
154 values_t values;
155 int fd;
157 static struct option longopts[] = {{"device", required_argument, 0, 'd'},
158 {"immediate", no_argument, 0, 'i'},
159 {"quiet-check", no_argument, 0, 'q'},
160 {"auto-on", no_argument, 0, '1'},
161 {"auto-off", no_argument, 0, '0'},
162 {"nagios", no_argument, 0, 'n'}, /* DEPRECATED, but we still accept it */
163 {"help", no_argument, 0, 'h'},
164 {"version", no_argument, 0, 'V'},
165 {0, 0, 0, 0}};
167 /* Parse extra opts if any */
168 argv = np_extra_opts(&argc, argv, progname);
170 setlocale(LC_ALL, "");
171 bindtextdomain(PACKAGE, LOCALEDIR);
172 textdomain(PACKAGE);
174 while (true) {
176 o = getopt_long(argc, argv, "+d:iq10nhVv", longopts, &longindex);
178 if (o == -1 || o == EOF || o == 1)
179 break;
181 switch (o) {
182 case 'd':
183 device = optarg;
184 break;
185 case 'q':
186 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -q switch (quiet output) is no longer \"quiet\"."));
187 fprintf(stderr, "%s\n", _("Nagios-compatible output is now always returned."));
188 break;
189 case 'i':
190 case '1':
191 case '0':
192 printf("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help)."));
193 return STATE_CRITICAL;
194 break;
195 case 'n':
196 fprintf(stderr, "%s\n", _("DEPRECATION WARNING: the -n switch (Nagios-compatible output) is now the"));
197 fprintf(stderr, "%s\n", _("default and will be removed from future releases."));
198 break;
199 case 'v': /* verbose */
200 verbose = true;
201 break;
202 case 'h':
203 print_help();
204 return STATE_UNKNOWN;
205 case 'V':
206 print_revision(progname, NP_VERSION);
207 return STATE_UNKNOWN;
208 default:
209 usage5();
213 if (optind < argc) {
214 device = argv[optind];
217 if (!device) {
218 print_help();
219 return STATE_UNKNOWN;
222 fd = open(device, OPEN_MODE);
224 if (fd < 0) {
225 printf(_("CRITICAL - Couldn't open device %s: %s\n"), device, strerror(errno));
226 return STATE_CRITICAL;
229 if (smart_cmd_simple(fd, SMART_CMD_ENABLE, 0, false)) {
230 printf(_("CRITICAL - SMART_CMD_ENABLE\n"));
231 return STATE_CRITICAL;
234 smart_read_values(fd, &values);
235 smart_read_thresholds(fd, &thresholds);
236 retval = nagios(&values, &thresholds);
237 if (verbose)
238 print_values(&values, &thresholds);
240 close(fd);
241 return retval;
244 char *get_offline_text(int status) {
245 int i;
246 for (i = 0; offline_status_text[i].text; i++) {
247 if (offline_status_text[i].value == status) {
248 return offline_status_text[i].text;
251 return "UNKNOWN";
254 int smart_read_values(int fd, values_t *values) {
255 #ifdef __linux__
256 int e;
257 __u8 args[4 + 512];
258 args[0] = WIN_SMART;
259 args[1] = 0;
260 args[2] = SMART_READ_VALUES;
261 args[3] = 1;
262 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
263 e = errno;
264 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
265 return e;
267 memcpy(values, args + 4, 512);
268 #endif /* __linux__ */
269 #ifdef __NetBSD__
270 struct atareq req;
271 unsigned char inbuf[DEV_BSIZE];
273 memset(&req, 0, sizeof(req));
274 req.timeout = 1000;
275 memset(&inbuf, 0, sizeof(inbuf));
277 req.flags = ATACMD_READ;
278 req.features = WDSM_RD_DATA;
279 req.command = WDCC_SMART;
280 req.databuf = (char *)inbuf;
281 req.datalen = sizeof(inbuf);
282 req.cylinder = WDSMART_CYL;
284 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
285 if (req.retsts != ATACMD_OK)
286 errno = ENODEV;
289 if (errno != 0) {
290 int e = errno;
291 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno));
292 return e;
295 (void)memcpy(values, inbuf, 512);
296 #endif /* __NetBSD__ */
297 return 0;
300 int nagios(values_t *p, thresholds_t *t) {
301 value_t *value = p->values;
302 threshold_t *threshold = t->thresholds;
303 int status = OPERATIONAL;
304 int prefailure = 0;
305 int advisory = 0;
306 int failed = 0;
307 int passed = 0;
308 int total = 0;
309 int i;
310 for (i = 0; i < NR_ATTRIBUTES; i++) {
311 if (value->id && threshold->id && value->id == threshold->id) {
312 if (value->value < threshold->threshold) {
313 ++failed;
314 if (value->status & 1) {
315 status = PREFAILURE;
316 ++prefailure;
317 } else {
318 status = ADVISORY;
319 ++advisory;
321 } else {
322 ++passed;
324 ++total;
326 ++value;
327 ++threshold;
329 switch (status) {
330 case PREFAILURE:
331 printf(_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), prefailure, prefailure > 1 ? 's' : ' ', failed,
332 total);
333 status = STATE_CRITICAL;
334 break;
335 case ADVISORY:
336 printf(_("WARNING - %d Harddrive Advisor%s Detected. %d/%d tests failed.\n"), advisory, advisory > 1 ? "ies" : "y", failed, total);
337 status = STATE_WARNING;
338 break;
339 case OPERATIONAL:
340 printf(_("OK - Operational (%d/%d tests passed)\n"), passed, total);
341 status = STATE_OK;
342 break;
343 default:
344 printf(_("ERROR - Status '%d' unknown. %d/%d tests passed\n"), status, passed, total);
345 status = STATE_UNKNOWN;
346 break;
348 return status;
351 void print_value(value_t *p, threshold_t *t) {
352 printf("Id=%3d, Status=%2d {%s , %s}, Value=%3d, Threshold=%3d, %s\n", p->id, p->status, p->status & 1 ? "PreFailure" : "Advisory ",
353 p->status & 2 ? "OnLine " : "OffLine", p->value, t->threshold, p->value >= t->threshold ? "Passed" : "Failed");
356 void print_values(values_t *p, thresholds_t *t) {
357 value_t *value = p->values;
358 threshold_t *threshold = t->thresholds;
359 int i;
360 for (i = 0; i < NR_ATTRIBUTES; i++) {
361 if (value->id && threshold->id && value->id == threshold->id) {
362 print_value(value++, threshold++);
365 printf(_("OffLineStatus=%d {%s}, AutoOffLine=%s, OffLineTimeout=%d minutes\n"), p->offline_status,
366 get_offline_text(p->offline_status & 0x7f), (p->offline_status & 0x80 ? "Yes" : "No"), p->offline_timeout / 60);
367 printf(_("OffLineCapability=%d {%s %s %s}\n"), p->offline_capability, p->offline_capability & 1 ? "Immediate" : "",
368 p->offline_capability & 2 ? "Auto" : "", p->offline_capability & 4 ? "AbortOnCmd" : "SuspendOnCmd");
369 printf(_("SmartRevision=%d, CheckSum=%d, SmartCapability=%d {%s %s}\n"), p->revision, p->checksum, p->smart_capability,
370 p->smart_capability & 1 ? "SaveOnStandBy" : "", p->smart_capability & 2 ? "AutoSave" : "");
373 int smart_cmd_simple(int fd, enum SmartCommand command, __u8 val0, bool show_error) {
374 int e = STATE_UNKNOWN;
375 #ifdef __linux__
376 __u8 args[4];
377 args[0] = WIN_SMART;
378 args[1] = val0;
379 args[2] = smart_command[command].value;
380 args[3] = 0;
381 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
382 e = STATE_CRITICAL;
383 if (show_error)
384 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
385 } else {
386 e = STATE_OK;
387 if (show_error)
388 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
391 #endif /* __linux__ */
392 #ifdef __NetBSD__
393 struct atareq req;
395 memset(&req, 0, sizeof(req));
396 req.timeout = 1000;
397 req.flags = ATACMD_READREG;
398 req.features = smart_command[command].value;
399 req.command = WDCC_SMART;
400 req.cylinder = WDSMART_CYL;
401 req.sec_count = val0;
403 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
404 if (req.retsts != ATACMD_OK)
405 errno = ENODEV;
406 if (req.cylinder != WDSMART_CYL)
407 errno = ENODEV;
410 if (errno != 0) {
411 e = STATE_CRITICAL;
412 if (show_error)
413 printf(_("CRITICAL - %s: %s\n"), smart_command[command].text, strerror(errno));
414 } else {
415 e = STATE_OK;
416 if (show_error)
417 printf(_("OK - Command sent (%s)\n"), smart_command[command].text);
420 #endif /* __NetBSD__ */
421 return e;
424 int smart_read_thresholds(int fd, thresholds_t *thresholds) {
425 #ifdef __linux__
426 int e;
427 __u8 args[4 + 512];
428 args[0] = WIN_SMART;
429 args[1] = 0;
430 args[2] = SMART_READ_THRESHOLDS;
431 args[3] = 1;
432 if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
433 e = errno;
434 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
435 return e;
437 memcpy(thresholds, args + 4, 512);
438 #endif /* __linux__ */
439 #ifdef __NetBSD__
440 struct atareq req;
441 unsigned char inbuf[DEV_BSIZE];
443 memset(&req, 0, sizeof(req));
444 req.timeout = 1000;
445 memset(&inbuf, 0, sizeof(inbuf));
447 req.flags = ATACMD_READ;
448 req.features = WDSM_RD_THRESHOLDS;
449 req.command = WDCC_SMART;
450 req.databuf = (char *)inbuf;
451 req.datalen = sizeof(inbuf);
452 req.cylinder = WDSMART_CYL;
454 if (ioctl(fd, ATAIOCCOMMAND, &req) == 0) {
455 if (req.retsts != ATACMD_OK)
456 errno = ENODEV;
459 if (errno != 0) {
460 int e = errno;
461 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno));
462 return e;
465 (void)memcpy(thresholds, inbuf, 512);
466 #endif /* __NetBSD__ */
467 return 0;
470 void print_help(void) {
471 print_revision(progname, NP_VERSION);
473 printf("(C) 1999 Ragnar Hojland Espinosa <ragnar@lightside.dhis.org>\n");
474 printf("Plugin implementation - 1999 Robert Dale <rdale@digital-mission.com>\n");
475 printf(COPYRIGHT, copyright, email);
477 printf(_("This plugin checks a local hard drive with the (Linux specific) SMART interface "
478 "[http://smartlinux.sourceforge.net/smart/index.php]."));
480 printf("\n\n");
482 print_usage();
484 printf(UT_HELP_VRSN);
485 printf(UT_EXTRA_OPTS);
487 printf(" %s\n", "-d, --device=DEVICE");
488 printf(" %s\n", _("Select device DEVICE"));
489 printf(" %s\n", _("Note: if the device is specified without this option, any further option will"));
490 printf(" %s\n", _("be ignored."));
492 printf(UT_VERBOSE);
494 printf("\n");
495 printf("%s\n", _("Notes:"));
496 printf(" %s\n", _("The SMART command modes (-i/--immediate, -0/--auto-off and -1/--auto-on) were"));
497 printf(" %s\n", _("broken in an underhand manner and have been disabled. You can use smartctl"));
498 printf(" %s\n", _("instead:"));
499 printf(" %s\n", _("-0/--auto-off: use \"smartctl --offlineauto=off\""));
500 printf(" %s\n", _("-1/--auto-on: use \"smartctl --offlineauto=on\""));
501 printf(" %s\n", _("-i/--immediate: use \"smartctl --test=offline\""));
503 printf(UT_SUPPORT);
506 /* todo : add to the long nanual as example
508 * Run with: check_ide-smart --nagios [-d] <DRIVE>
509 * Where DRIVE is an IDE drive, ie. /dev/hda, /dev/hdb, /dev/hdc
511 * - Returns 0 on no errors
512 * - Returns 1 on advisories
513 * - Returns 2 on prefailure
514 * - Returns -1 not too often
517 void print_usage(void) {
518 printf("%s\n", _("Usage:"));
519 printf("%s [-d <device>] [-v]", progname);