1 /*****************************************************************************
3 * Monitoring check_ide_smart plugin
4 * ide-smart 1.3 - IDE S.M.A.R.T. checking tool
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
14 * This file contains the check_ide_smart plugin
16 * This plugin checks a local hard drive with the (Linux specific) SMART
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";
43 static void print_help(void);
44 void print_usage(void);
47 #include <sys/ioctl.h>
50 # include <linux/hdreg.h>
51 # include <linux/types.h>
53 # define OPEN_MODE O_RDONLY
54 #endif /* __linux__ */
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__ */
74 #define NR_ATTRIBUTES 30
81 typedef struct threshold_s
{
85 } __attribute__((packed
)) threshold_t
;
87 typedef struct thresholds_s
{
89 threshold_t thresholds
[NR_ATTRIBUTES
];
93 } __attribute__((packed
)) thresholds_t
;
95 typedef struct value_s
{
100 } __attribute__((packed
)) value_t
;
102 typedef struct values_s
{
104 value_t values
[NR_ATTRIBUTES
];
107 __u16 offline_timeout
;
109 __u8 offline_capability
;
110 __u16 smart_capability
;
114 } __attribute__((packed
)) values_t
;
119 } offline_status_text
[] = {{0x00, "NeverStarted"}, {0x02, "Completed"}, {0x04, "Suspended"},
120 {0x05, "Aborted"}, {0x06, "Failed"}, {0, 0}};
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 */
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
[]) {
153 thresholds_t thresholds
;
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'},
167 /* Parse extra opts if any */
168 argv
= np_extra_opts(&argc
, argv
, progname
);
170 setlocale(LC_ALL
, "");
171 bindtextdomain(PACKAGE
, LOCALEDIR
);
176 o
= getopt_long(argc
, argv
, "+d:iq10nhVv", longopts
, &longindex
);
178 if (o
== -1 || o
== EOF
|| o
== 1)
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."));
192 printf("%s\n", _("SMART commands are broken and have been disabled (See Notes in --help)."));
193 return STATE_CRITICAL
;
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."));
199 case 'v': /* verbose */
204 return STATE_UNKNOWN
;
206 print_revision(progname
, NP_VERSION
);
207 return STATE_UNKNOWN
;
214 device
= argv
[optind
];
219 return STATE_UNKNOWN
;
222 fd
= open(device
, OPEN_MODE
);
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
);
238 print_values(&values
, &thresholds
);
244 char *get_offline_text(int status
) {
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
;
254 int smart_read_values(int fd
, values_t
*values
) {
260 args
[2] = SMART_READ_VALUES
;
262 if (ioctl(fd
, HDIO_DRIVE_CMD
, &args
)) {
264 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno
));
267 memcpy(values
, args
+ 4, 512);
268 #endif /* __linux__ */
271 unsigned char inbuf
[DEV_BSIZE
];
273 memset(&req
, 0, sizeof(req
));
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
)
291 printf(_("CRITICAL - SMART_READ_VALUES: %s\n"), strerror(errno
));
295 (void)memcpy(values
, inbuf
, 512);
296 #endif /* __NetBSD__ */
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
;
310 for (i
= 0; i
< NR_ATTRIBUTES
; i
++) {
311 if (value
->id
&& threshold
->id
&& value
->id
== threshold
->id
) {
312 if (value
->value
< threshold
->threshold
) {
314 if (value
->status
& 1) {
331 printf(_("CRITICAL - %d Harddrive PreFailure%cDetected! %d/%d tests failed.\n"), prefailure
, prefailure
> 1 ? 's' : ' ', failed
,
333 status
= STATE_CRITICAL
;
336 printf(_("WARNING - %d Harddrive Advisor%s Detected. %d/%d tests failed.\n"), advisory
, advisory
> 1 ? "ies" : "y", failed
, total
);
337 status
= STATE_WARNING
;
340 printf(_("OK - Operational (%d/%d tests passed)\n"), passed
, total
);
344 printf(_("ERROR - Status '%d' unknown. %d/%d tests passed\n"), status
, passed
, total
);
345 status
= STATE_UNKNOWN
;
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
;
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
;
379 args
[2] = smart_command
[command
].value
;
381 if (ioctl(fd
, HDIO_DRIVE_CMD
, &args
)) {
384 printf(_("CRITICAL - %s: %s\n"), smart_command
[command
].text
, strerror(errno
));
388 printf(_("OK - Command sent (%s)\n"), smart_command
[command
].text
);
391 #endif /* __linux__ */
395 memset(&req
, 0, sizeof(req
));
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
)
406 if (req
.cylinder
!= WDSMART_CYL
)
413 printf(_("CRITICAL - %s: %s\n"), smart_command
[command
].text
, strerror(errno
));
417 printf(_("OK - Command sent (%s)\n"), smart_command
[command
].text
);
420 #endif /* __NetBSD__ */
424 int smart_read_thresholds(int fd
, thresholds_t
*thresholds
) {
430 args
[2] = SMART_READ_THRESHOLDS
;
432 if (ioctl(fd
, HDIO_DRIVE_CMD
, &args
)) {
434 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno
));
437 memcpy(thresholds
, args
+ 4, 512);
438 #endif /* __linux__ */
441 unsigned char inbuf
[DEV_BSIZE
];
443 memset(&req
, 0, sizeof(req
));
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
)
461 printf(_("CRITICAL - SMART_READ_THRESHOLDS: %s\n"), strerror(errno
));
465 (void)memcpy(thresholds
, inbuf
, 512);
466 #endif /* __NetBSD__ */
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]."));
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."));
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\""));
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
);