1 /*****************************************************************************
3 * Monitoring check_disk plugin
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
10 * This file contains the check_disk plugin
13 * This program is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 *****************************************************************************/
29 const char *progname
= "check_disk";
30 const char *program_name
= "check_disk"; /* Required for coreutils libs */
31 const char *copyright
= "1999-2024";
32 const char *email
= "devel@monitoring-plugins.org";
35 #ifdef HAVE_SYS_STAT_H
36 # include <sys/stat.h>
39 # include <inttypes.h>
44 #include "utils_disk.h"
47 #include "mountlist.h"
60 /* If nonzero, show even filesystems with zero size or
61 uninteresting types. */
62 static int show_all_fs
= 1;
64 /* If nonzero, show only local filesystems. */
65 static int show_local_fs
= 0;
67 /* If nonzero, show only local filesystems but call stat() on remote ones. */
68 static int stat_remote_fs
= 0;
70 /* If positive, the units to use when printing sizes;
71 if negative, the human-readable base. */
72 /* static int output_block_size; */
74 /* If nonzero, invoke the `sync' system call before getting any usage data.
75 Using this option can make df very slow, especially with many or very
76 busy disks. Note that this may make a difference on some systems --
77 SunOs4.1.3, for one. It is *not* necessary on Linux. */
78 /* static int require_sync = 0; */
80 /* Linked list of filesystem types to display.
81 If `fs_select_list' is NULL, list all types.
82 This table is generated dynamically from command-line options,
83 rather than hardcoding into the program what it thinks are the
84 valid filesystem types; let the user specify any filesystem type
85 they want to, and if there are any filesystems of that type, they
88 Some filesystem types:
89 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
91 /* static struct parameter_list *fs_select_list; */
93 /* Linked list of filesystem types to omit.
94 If the list is empty, don't exclude any types. */
95 static struct regex_list
*fs_exclude_list
= NULL
;
97 /* Linked list of filesystem types to check.
98 If the list is empty, include all types. */
99 static struct regex_list
*fs_include_list
;
101 static struct name_list
*dp_exclude_list
;
103 static struct parameter_list
*path_select_list
= NULL
;
105 /* Linked list of mounted filesystems. */
106 static struct mount_entry
*mount_list
;
108 /* For long options that have no equivalent short option, use a
109 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
111 SYNC_OPTION
= CHAR_MAX
+ 1,
120 static int process_arguments(int /*argc*/, char ** /*argv*/);
121 static void set_all_thresholds(struct parameter_list
*path
);
122 static void print_help(void);
123 void print_usage(void);
124 static double calculate_percent(uintmax_t, uintmax_t);
125 static bool stat_path(struct parameter_list
*p
);
126 static void get_stats(struct parameter_list
*p
, struct fs_usage
*fsp
);
127 static void get_path_stats(struct parameter_list
*p
, struct fs_usage
*fsp
);
130 static uintmax_t mult
= 1024 * 1024;
131 static int verbose
= 0;
132 static bool erronly
= false;
133 static bool display_mntp
= false;
134 static bool exact_match
= false;
135 static bool ignore_missing
= false;
136 static bool freespace_ignore_reserved
= false;
137 static bool display_inodes_perfdata
= false;
138 static char *warn_freespace_units
= NULL
;
139 static char *crit_freespace_units
= NULL
;
140 static char *warn_freespace_percent
= NULL
;
141 static char *crit_freespace_percent
= NULL
;
142 static char *warn_usedspace_units
= NULL
;
143 static char *crit_usedspace_units
= NULL
;
144 static char *warn_usedspace_percent
= NULL
;
145 static char *crit_usedspace_percent
= NULL
;
146 static char *warn_usedinodes_percent
= NULL
;
147 static char *crit_usedinodes_percent
= NULL
;
148 static char *warn_freeinodes_percent
= NULL
;
149 static char *crit_freeinodes_percent
= NULL
;
150 static bool path_selected
= false;
151 static bool path_ignored
= false;
152 static char *group
= NULL
;
153 static struct stat
*stat_buf
;
154 static struct name_list
*seen
= NULL
;
156 int main(int argc
, char **argv
) {
157 int result
= STATE_UNKNOWN
;
158 int disk_result
= STATE_UNKNOWN
;
160 char *ignored
= NULL
;
161 char *details
= NULL
;
163 char *perf_ilabel
= NULL
;
164 char *preamble
= " - free space:";
165 char *ignored_preamble
= " - ignored paths:";
166 char *flag_header
= NULL
;
167 int temp_result
= STATE_UNKNOWN
;
169 struct mount_entry
*me
= NULL
;
170 struct fs_usage fsp
= {0};
171 struct parameter_list
*temp_list
= NULL
;
172 struct parameter_list
*path
= NULL
;
179 ignored
= strdup("");
180 details
= strdup("");
182 perf_ilabel
= strdup("");
183 stat_buf
= malloc(sizeof *stat_buf
);
185 setlocale(LC_ALL
, "");
186 bindtextdomain(PACKAGE
, LOCALEDIR
);
189 mount_list
= read_file_system_list(0);
191 /* Parse extra opts if any */
192 argv
= np_extra_opts(&argc
, argv
, progname
);
194 if (process_arguments(argc
, argv
) == ERROR
)
195 usage4(_("Could not parse arguments"));
197 /* If a list of paths has not been selected, find entire
198 mount list and create list of paths
200 if (path_selected
== false && path_ignored
== false) {
201 for (me
= mount_list
; me
; me
= me
->me_next
) {
202 if (!(path
= np_find_parameter(path_select_list
, me
->me_mountdir
))) {
203 path
= np_add_parameter(&path_select_list
, me
->me_mountdir
);
205 path
->best_match
= me
;
207 set_all_thresholds(path
);
211 if (path_ignored
== false) {
212 np_set_best_match(path_select_list
, mount_list
, exact_match
);
215 /* Error if no match found for specified paths */
216 temp_list
= path_select_list
;
218 while (path_select_list
) {
219 if (!path_select_list
->best_match
&& ignore_missing
== true) {
220 /* If the first element will be deleted, the temp_list must be updated with the new start address as well */
221 if (path_select_list
== temp_list
) {
222 temp_list
= path_select_list
->name_next
;
224 /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */
225 xasprintf(&ignored
, "%s %s;", ignored
, path_select_list
->name
);
226 /* Delete the path from the list so that it is not stat-checked later in the code. */
227 path_select_list
= np_del_parameter(path_select_list
, path_select_list
->name_prev
);
228 } else if (!path_select_list
->best_match
) {
229 /* Without --ignore-missing option, exit with Critical state. */
230 die(STATE_CRITICAL
, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list
->name
);
232 /* Continue jumping through the list */
233 path_select_list
= path_select_list
->name_next
;
237 path_select_list
= temp_list
;
239 if (!path_select_list
&& ignore_missing
== true) {
242 printf("None of the provided paths were found\n");
246 /* Process for every path in list */
247 for (path
= path_select_list
; path
; path
= path
->name_next
) {
248 if (verbose
>= 3 && path
->freespace_percent
->warning
!= NULL
&& path
->freespace_percent
->critical
!= NULL
)
249 printf("Thresholds(pct) for %s warn: %f crit %f\n", path
->name
, path
->freespace_percent
->warning
->end
,
250 path
->freespace_percent
->critical
->end
);
252 if (verbose
>= 3 && path
->group
!= NULL
)
253 printf("Group of %s: %s\n", path
->name
, path
->group
);
255 /* reset disk result */
256 disk_result
= STATE_UNKNOWN
;
258 me
= path
->best_match
;
265 if (strncmp(path
->name
, "/cygdrive/", 10) != 0 || strlen(path
->name
) > 11)
267 snprintf(mountdir
, sizeof(mountdir
), "%s:\\", me
->me_mountdir
+ 10);
268 if (GetDriveType(mountdir
) != DRIVE_FIXED
)
273 /* Remove filesystems already seen */
274 if (np_seen_name(seen
, me
->me_mountdir
)) {
277 np_add_name(&seen
, me
->me_mountdir
);
279 if (path
->group
== NULL
) {
280 /* Skip remote filesystems if we're not interested in them */
281 if (me
->me_remote
&& show_local_fs
) {
282 if (stat_remote_fs
) {
283 if (!stat_path(path
) && ignore_missing
== true) {
285 xasprintf(&ignored
, "%s %s;", ignored
, path
->name
);
289 /* Skip pseudo fs's if we haven't asked for all fs's */
291 if (me
->me_dummy
&& !show_all_fs
) {
293 /* Skip excluded fstypes */
295 if (fs_exclude_list
&& np_find_regmatch(fs_exclude_list
, me
->me_type
)) {
297 /* Skip excluded fs's */
299 if (dp_exclude_list
&& (np_find_name(dp_exclude_list
, me
->me_devname
) || np_find_name(dp_exclude_list
, me
->me_mountdir
))) {
301 /* Skip not included fstypes */
303 if (fs_include_list
&& !np_find_regmatch(fs_include_list
, me
->me_type
)) {
308 if (!stat_path(path
)) {
309 if (ignore_missing
== true) {
311 xasprintf(&ignored
, "%s %s;", ignored
, path
->name
);
315 get_fs_usage(me
->me_mountdir
, me
->me_devname
, &fsp
);
317 if (fsp
.fsu_blocks
&& strcmp("none", me
->me_mountdir
)) {
318 get_stats(path
, &fsp
);
321 printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f "
322 "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n",
323 me
->me_mountdir
, path
->dused_pct
, path
->dfree_pct
, path
->dused_units
, path
->dfree_units
, path
->dtotal_units
,
324 path
->dused_inodes_percent
, path
->dfree_inodes_percent
, fsp
.fsu_blocksize
, mult
);
327 /* Threshold comparisons */
329 temp_result
= get_status(path
->dfree_units
, path
->freespace_units
);
331 printf("Freespace_units result=%d\n", temp_result
);
332 disk_result
= max_state(disk_result
, temp_result
);
334 temp_result
= get_status(path
->dfree_pct
, path
->freespace_percent
);
336 printf("Freespace%% result=%d\n", temp_result
);
337 disk_result
= max_state(disk_result
, temp_result
);
339 temp_result
= get_status(path
->dused_units
, path
->usedspace_units
);
341 printf("Usedspace_units result=%d\n", temp_result
);
342 disk_result
= max_state(disk_result
, temp_result
);
344 temp_result
= get_status(path
->dused_pct
, path
->usedspace_percent
);
346 printf("Usedspace_percent result=%d\n", temp_result
);
347 disk_result
= max_state(disk_result
, temp_result
);
349 temp_result
= get_status(path
->dused_inodes_percent
, path
->usedinodes_percent
);
351 printf("Usedinodes_percent result=%d\n", temp_result
);
352 disk_result
= max_state(disk_result
, temp_result
);
354 temp_result
= get_status(path
->dfree_inodes_percent
, path
->freeinodes_percent
);
356 printf("Freeinodes_percent result=%d\n", temp_result
);
357 disk_result
= max_state(disk_result
, temp_result
);
359 result
= max_state(result
, disk_result
);
361 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
362 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
363 data. Assumption that start=0. Roll on new syntax...
366 /* *_high_tide must be reinitialized at each run */
367 uint64_t warning_high_tide
= UINT64_MAX
;
369 if (path
->freespace_units
->warning
!= NULL
) {
370 warning_high_tide
= (path
->dtotal_units
- path
->freespace_units
->warning
->end
) * mult
;
372 if (path
->freespace_percent
->warning
!= NULL
) {
374 min(warning_high_tide
, (uint64_t)((1.0 - path
->freespace_percent
->warning
->end
/ 100) * (path
->dtotal_units
* mult
)));
377 uint64_t critical_high_tide
= UINT64_MAX
;
379 if (path
->freespace_units
->critical
!= NULL
) {
380 critical_high_tide
= (path
->dtotal_units
- path
->freespace_units
->critical
->end
) * mult
;
382 if (path
->freespace_percent
->critical
!= NULL
) {
384 min(critical_high_tide
, (uint64_t)((1.0 - path
->freespace_percent
->critical
->end
/ 100) * (path
->dtotal_units
* mult
)));
387 /* Nb: *_high_tide are unset when == UINT64_MAX */
388 xasprintf(&perf
, "%s %s", perf
,
389 perfdata_uint64((!strcmp(me
->me_mountdir
, "none") || display_mntp
) ? me
->me_devname
: me
->me_mountdir
,
390 path
->dused_units
* mult
, "B", (warning_high_tide
== UINT64_MAX
? false : true), warning_high_tide
,
391 (critical_high_tide
== UINT64_MAX
? false : true), critical_high_tide
, true, 0, true,
392 path
->dtotal_units
* mult
));
394 if (display_inodes_perfdata
) {
395 /* *_high_tide must be reinitialized at each run */
396 warning_high_tide
= UINT64_MAX
;
397 critical_high_tide
= UINT64_MAX
;
399 if (path
->freeinodes_percent
->warning
!= NULL
) {
400 warning_high_tide
= (uint64_t)fabs(
401 min((double)warning_high_tide
, (double)(1.0 - path
->freeinodes_percent
->warning
->end
/ 100) * path
->inodes_total
));
403 if (path
->freeinodes_percent
->critical
!= NULL
) {
404 critical_high_tide
= (uint64_t)fabs(min(
405 (double)critical_high_tide
, (double)(1.0 - path
->freeinodes_percent
->critical
->end
/ 100) * path
->inodes_total
));
408 xasprintf(&perf_ilabel
, "%s (inodes)",
409 (!strcmp(me
->me_mountdir
, "none") || display_mntp
) ? me
->me_devname
: me
->me_mountdir
);
410 /* Nb: *_high_tide are unset when == UINT64_MAX */
411 xasprintf(&perf
, "%s %s", perf
,
412 perfdata_uint64(perf_ilabel
, path
->inodes_used
, "", (warning_high_tide
!= UINT64_MAX
? true : false),
413 warning_high_tide
, (critical_high_tide
!= UINT64_MAX
? true : false), critical_high_tide
, true, 0,
414 true, path
->inodes_total
));
417 if (disk_result
== STATE_OK
&& erronly
&& !verbose
)
420 if (disk_result
&& verbose
>= 1) {
421 xasprintf(&flag_header
, " %s [", state_text(disk_result
));
423 xasprintf(&flag_header
, "");
425 xasprintf(&output
, "%s%s %s %llu%s (%.1f%%", output
, flag_header
,
426 (!strcmp(me
->me_mountdir
, "none") || display_mntp
) ? me
->me_devname
: me
->me_mountdir
, path
->dfree_units
, units
,
428 if (path
->dused_inodes_percent
< 0) {
429 xasprintf(&output
, "%s inode=-)%s;", output
, (disk_result
? "]" : ""));
431 xasprintf(&output
, "%s inode=%.0f%%)%s;", output
, path
->dfree_inodes_percent
, ((disk_result
&& verbose
>= 1) ? "]" : ""));
438 xasprintf(&output
, "%s%s", output
, details
);
440 if (strcmp(output
, "") == 0 && !erronly
) {
442 xasprintf(&output
, " - No disks were found for provided parameters");
445 printf("DISK %s%s%s%s%s|%s\n", state_text(result
), ((erronly
&& result
== STATE_OK
)) ? "" : preamble
, output
,
446 (strcmp(ignored
, "") == 0) ? "" : ignored_preamble
, ignored
, perf
);
450 double calculate_percent(uintmax_t value
, uintmax_t total
) {
452 if (value
<= DBL_MAX
&& total
!= 0) {
453 pct
= (double)value
/ total
* 100.0;
458 /* process command-line arguments */
459 int process_arguments(int argc
, char **argv
) {
462 struct parameter_list
*se
;
463 struct parameter_list
*temp_list
= NULL
;
464 struct parameter_list
*previous
= NULL
;
465 struct mount_entry
*me
;
467 int cflags
= REG_NOSUB
| REG_EXTENDED
;
468 int default_cflags
= cflags
;
469 char errbuf
[MAX_INPUT_BUFFER
];
473 static struct option longopts
[] = {{"timeout", required_argument
, 0, 't'},
474 {"warning", required_argument
, 0, 'w'},
475 {"critical", required_argument
, 0, 'c'},
476 {"iwarning", required_argument
, 0, 'W'},
477 /* Dang, -C is taken. We might want to reshuffle this. */
478 {"icritical", required_argument
, 0, 'K'},
479 {"kilobytes", no_argument
, 0, 'k'},
480 {"megabytes", no_argument
, 0, 'm'},
481 {"units", required_argument
, 0, 'u'},
482 {"path", required_argument
, 0, 'p'},
483 {"partition", required_argument
, 0, 'p'},
484 {"exclude_device", required_argument
, 0, 'x'},
485 {"exclude-type", required_argument
, 0, 'X'},
486 {"include-type", required_argument
, 0, 'N'},
487 {"group", required_argument
, 0, 'g'},
488 {"eregi-path", required_argument
, 0, 'R'},
489 {"eregi-partition", required_argument
, 0, 'R'},
490 {"ereg-path", required_argument
, 0, 'r'},
491 {"ereg-partition", required_argument
, 0, 'r'},
492 {"freespace-ignore-reserved", no_argument
, 0, 'f'},
493 {"ignore-ereg-path", required_argument
, 0, 'i'},
494 {"ignore-ereg-partition", required_argument
, 0, 'i'},
495 {"ignore-eregi-path", required_argument
, 0, 'I'},
496 {"ignore-eregi-partition", required_argument
, 0, 'I'},
497 {"ignore-missing", no_argument
, 0, 'n'},
498 {"local", no_argument
, 0, 'l'},
499 {"stat-remote-fs", no_argument
, 0, 'L'},
500 {"iperfdata", no_argument
, 0, 'P'},
501 {"mountpoint", no_argument
, 0, 'M'},
502 {"errors-only", no_argument
, 0, 'e'},
503 {"exact-match", no_argument
, 0, 'E'},
504 {"all", no_argument
, 0, 'A'},
505 {"verbose", no_argument
, 0, 'v'},
506 {"quiet", no_argument
, 0, 'q'},
507 {"clear", no_argument
, 0, 'C'},
508 {"version", no_argument
, 0, 'V'},
509 {"help", no_argument
, 0, 'h'},
515 np_add_regex(&fs_exclude_list
, "iso9660", REG_EXTENDED
);
517 for (c
= 1; c
< argc
; c
++)
518 if (strcmp("-to", argv
[c
]) == 0)
519 strcpy(argv
[c
], "-t");
522 c
= getopt_long(argc
, argv
, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts
, &option
);
524 if (c
== -1 || c
== EOF
)
528 case 't': /* timeout period */
529 if (is_integer(optarg
)) {
530 timeout_interval
= atoi(optarg
);
533 usage2(_("Timeout interval must be a positive integer"), optarg
);
536 /* See comments for 'c' */
537 case 'w': /* warning threshold */
538 if (!is_percentage_expression(optarg
) && !is_numeric(optarg
)) {
539 die(STATE_UNKNOWN
, "Argument for --warning invalid or missing: %s\n", optarg
);
542 if (strstr(optarg
, "%")) {
543 if (*optarg
== '@') {
544 warn_freespace_percent
= optarg
;
546 xasprintf(&warn_freespace_percent
, "@%s", optarg
);
549 if (*optarg
== '@') {
550 warn_freespace_units
= optarg
;
552 xasprintf(&warn_freespace_units
, "@%s", optarg
);
557 /* Awful mistake where the range values do not make sense. Normally,
558 you alert if the value is within the range, but since we are using
559 freespace, we have to alert if outside the range. Thus we artificially
560 force @ at the beginning of the range, so that it is backwards compatible
562 case 'c': /* critical threshold */
563 if (!is_percentage_expression(optarg
) && !is_numeric(optarg
)) {
564 die(STATE_UNKNOWN
, "Argument for --critical invalid or missing: %s\n", optarg
);
567 if (strstr(optarg
, "%")) {
568 if (*optarg
== '@') {
569 crit_freespace_percent
= optarg
;
571 xasprintf(&crit_freespace_percent
, "@%s", optarg
);
574 if (*optarg
== '@') {
575 crit_freespace_units
= optarg
;
577 xasprintf(&crit_freespace_units
, "@%s", optarg
);
582 case 'W': /* warning inode threshold */
583 if (*optarg
== '@') {
584 warn_freeinodes_percent
= optarg
;
586 xasprintf(&warn_freeinodes_percent
, "@%s", optarg
);
589 case 'K': /* critical inode threshold */
590 if (*optarg
== '@') {
591 crit_freeinodes_percent
= optarg
;
593 xasprintf(&crit_freeinodes_percent
, "@%s", optarg
);
599 if (!strcasecmp(optarg
, "bytes")) {
602 } else if (!strcmp(optarg
, "KiB")) {
603 mult
= (uintmax_t)1024;
604 units
= strdup("KiB");
605 } else if (!strcmp(optarg
, "kB")) {
606 mult
= (uintmax_t)1000;
607 units
= strdup("kB");
608 } else if (!strcmp(optarg
, "MiB")) {
609 mult
= (uintmax_t)1024 * 1024;
610 units
= strdup("MiB");
611 } else if (!strcmp(optarg
, "MB")) {
612 mult
= (uintmax_t)1000 * 1000;
613 units
= strdup("MB");
614 } else if (!strcmp(optarg
, "GiB")) {
615 mult
= (uintmax_t)1024 * 1024 * 1024;
616 units
= strdup("GiB");
617 } else if (!strcmp(optarg
, "GB")) {
618 mult
= (uintmax_t)1000 * 1000 * 1000;
619 units
= strdup("GB");
620 } else if (!strcmp(optarg
, "TiB")) {
621 mult
= (uintmax_t)1024 * 1024 * 1024 * 1024;
622 units
= strdup("TiB");
623 } else if (!strcmp(optarg
, "TB")) {
624 mult
= (uintmax_t)1000 * 1000 * 1000 * 1000;
625 units
= strdup("TB");
626 } else if (!strcmp(optarg
, "PiB")) {
627 mult
= (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024;
628 units
= strdup("PiB");
629 } else if (!strcmp(optarg
, "PB")) {
630 mult
= (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000;
631 units
= strdup("PB");
633 die(STATE_UNKNOWN
, _("unit type %s not known\n"), optarg
);
636 die(STATE_UNKNOWN
, _("failed allocating storage for '%s'\n"), "units");
638 case 'k': /* display mountpoint */
642 units
= strdup("kiB");
644 case 'm': /* display mountpoint */
648 units
= strdup("MiB");
657 display_inodes_perfdata
= 1;
659 case 'p': /* select path */
660 if (!(warn_freespace_units
|| crit_freespace_units
|| warn_freespace_percent
|| crit_freespace_percent
||
661 warn_usedspace_units
|| crit_usedspace_units
|| warn_usedspace_percent
|| crit_usedspace_percent
||
662 warn_usedinodes_percent
|| crit_usedinodes_percent
|| warn_freeinodes_percent
|| crit_freeinodes_percent
)) {
663 die(STATE_UNKNOWN
, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
666 /* add parameter if not found. overwrite thresholds if path has already been added */
667 if (!(se
= np_find_parameter(path_select_list
, optarg
))) {
668 se
= np_add_parameter(&path_select_list
, optarg
);
670 if (stat(optarg
, &stat_buf
[0]) && ignore_missing
== true) {
676 set_all_thresholds(se
);
678 /* With autofs, it is required to stat() the path before re-populating the mount_list */
679 if (!stat_path(se
)) {
682 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
683 * pointers are copied around. One of the reason it wasn't done yet is that other parts
684 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
685 mount_list
= read_file_system_list(0);
686 np_set_best_match(se
, mount_list
, exact_match
);
688 path_selected
= true;
690 case 'x': /* exclude path or partition */
691 np_add_name(&dp_exclude_list
, optarg
);
693 case 'X': /* exclude file system type */
694 err
= np_add_regex(&fs_exclude_list
, optarg
, REG_EXTENDED
);
696 regerror(err
, &fs_exclude_list
->regex
, errbuf
, MAX_INPUT_BUFFER
);
697 die(STATE_UNKNOWN
, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf
);
700 case 'N': /* include file system type */
701 err
= np_add_regex(&fs_include_list
, optarg
, REG_EXTENDED
);
703 regerror(err
, &fs_exclude_list
->regex
, errbuf
, MAX_INPUT_BUFFER
);
704 die(STATE_UNKNOWN
, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf
);
707 case 'v': /* verbose */
710 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
711 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
719 die(STATE_UNKNOWN
, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
723 freespace_ignore_reserved
= true;
727 die(STATE_UNKNOWN
, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
732 // Intentional fallthrough
735 die(STATE_UNKNOWN
, "DISK %s: %s\n", _("UNKNOWN"),
736 _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
737 err
= regcomp(&re
, optarg
, cflags
);
739 regerror(err
, &re
, errbuf
, MAX_INPUT_BUFFER
);
740 die(STATE_UNKNOWN
, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf
);
743 temp_list
= path_select_list
;
747 if (temp_list
->best_match
) {
748 if (np_regex_match_mount_entry(temp_list
->best_match
, &re
)) {
751 printf("ignoring %s matching regex\n", temp_list
->name
);
753 temp_list
= np_del_parameter(temp_list
, previous
);
754 /* pointer to first element needs to be updated if first item gets deleted */
755 if (previous
== NULL
)
756 path_select_list
= temp_list
;
758 previous
= temp_list
;
759 temp_list
= temp_list
->name_next
;
762 previous
= temp_list
;
763 temp_list
= temp_list
->name_next
;
767 cflags
= default_cflags
;
771 ignore_missing
= true;
774 optarg
= strdup(".*");
775 // Intentional fallthrough
778 // Intentional fallthrough
780 if (!(warn_freespace_units
|| crit_freespace_units
|| warn_freespace_percent
|| crit_freespace_percent
||
781 warn_usedspace_units
|| crit_usedspace_units
|| warn_usedspace_percent
|| crit_usedspace_percent
||
782 warn_usedinodes_percent
|| crit_usedinodes_percent
|| warn_freeinodes_percent
|| crit_freeinodes_percent
)) {
783 die(STATE_UNKNOWN
, "DISK %s: %s", _("UNKNOWN"),
784 _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n"));
787 err
= regcomp(&re
, optarg
, cflags
);
789 regerror(err
, &re
, errbuf
, MAX_INPUT_BUFFER
);
790 die(STATE_UNKNOWN
, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf
);
793 for (me
= mount_list
; me
; me
= me
->me_next
) {
794 if (np_regex_match_mount_entry(me
, &re
)) {
797 printf("%s %s matching expression %s\n", me
->me_devname
, me
->me_mountdir
, optarg
);
799 /* add parameter if not found. overwrite thresholds if path has already been added */
800 if (!(se
= np_find_parameter(path_select_list
, me
->me_mountdir
))) {
801 se
= np_add_parameter(&path_select_list
, me
->me_mountdir
);
804 set_all_thresholds(se
);
808 if (!fnd
&& ignore_missing
== true) {
810 path_selected
= true;
814 die(STATE_UNKNOWN
, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg
);
817 path_selected
= true;
818 np_set_best_match(path_select_list
, mount_list
, exact_match
);
819 cflags
= default_cflags
;
822 case 'M': /* display mountpoint */
826 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
827 if (path_selected
== false) {
828 struct parameter_list
*path
;
829 for (me
= mount_list
; me
; me
= me
->me_next
) {
830 if (!(path
= np_find_parameter(path_select_list
, me
->me_mountdir
)))
831 path
= np_add_parameter(&path_select_list
, me
->me_mountdir
);
832 path
->best_match
= me
;
834 set_all_thresholds(path
);
837 warn_freespace_units
= NULL
;
838 crit_freespace_units
= NULL
;
839 warn_usedspace_units
= NULL
;
840 crit_usedspace_units
= NULL
;
841 warn_freespace_percent
= NULL
;
842 crit_freespace_percent
= NULL
;
843 warn_usedspace_percent
= NULL
;
844 crit_usedspace_percent
= NULL
;
845 warn_usedinodes_percent
= NULL
;
846 crit_usedinodes_percent
= NULL
;
847 warn_freeinodes_percent
= NULL
;
848 crit_freeinodes_percent
= NULL
;
850 path_selected
= false;
853 case 'V': /* version */
854 print_revision(progname
, NP_VERSION
);
860 usage(_("Unknown argument"));
864 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
866 if (warn_usedspace_percent
== NULL
&& argc
> c
&& is_intnonneg(argv
[c
]))
867 warn_usedspace_percent
= argv
[c
++];
869 if (crit_usedspace_percent
== NULL
&& argc
> c
&& is_intnonneg(argv
[c
]))
870 crit_usedspace_percent
= argv
[c
++];
873 se
= np_add_parameter(&path_select_list
, strdup(argv
[c
++]));
874 path_selected
= true;
875 set_all_thresholds(se
);
879 units
= strdup("MiB");
880 mult
= (uintmax_t)1024 * 1024;
886 void set_all_thresholds(struct parameter_list
*path
) {
887 if (path
->freespace_units
!= NULL
)
888 free(path
->freespace_units
);
889 set_thresholds(&path
->freespace_units
, warn_freespace_units
, crit_freespace_units
);
890 if (path
->freespace_percent
!= NULL
)
891 free(path
->freespace_percent
);
892 set_thresholds(&path
->freespace_percent
, warn_freespace_percent
, crit_freespace_percent
);
893 if (path
->usedspace_units
!= NULL
)
894 free(path
->usedspace_units
);
895 set_thresholds(&path
->usedspace_units
, warn_usedspace_units
, crit_usedspace_units
);
896 if (path
->usedspace_percent
!= NULL
)
897 free(path
->usedspace_percent
);
898 set_thresholds(&path
->usedspace_percent
, warn_usedspace_percent
, crit_usedspace_percent
);
899 if (path
->usedinodes_percent
!= NULL
)
900 free(path
->usedinodes_percent
);
901 set_thresholds(&path
->usedinodes_percent
, warn_usedinodes_percent
, crit_usedinodes_percent
);
902 if (path
->freeinodes_percent
!= NULL
)
903 free(path
->freeinodes_percent
);
904 set_thresholds(&path
->freeinodes_percent
, warn_freeinodes_percent
, crit_freeinodes_percent
);
907 void print_help(void) {
908 print_revision(progname
, NP_VERSION
);
910 printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
911 printf(COPYRIGHT
, copyright
, email
);
913 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
914 printf("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
920 printf(UT_HELP_VRSN
);
921 printf(UT_EXTRA_OPTS
);
923 printf(" %s\n", "-w, --warning=INTEGER");
924 printf(" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
925 printf(" %s\n", "-w, --warning=PERCENT%");
926 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
927 printf(" %s\n", "-c, --critical=INTEGER");
928 printf(" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
929 printf(" %s\n", "-c, --critical=PERCENT%");
930 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
931 printf(" %s\n", "-W, --iwarning=PERCENT%");
932 printf(" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
933 printf(" %s\n", "-K, --icritical=PERCENT%");
934 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
935 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION");
936 printf(" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
937 printf(" %s\n", "-x, --exclude_device=PATH <STRING>");
938 printf(" %s\n", _("Ignore device (only works if -p unspecified)"));
939 printf(" %s\n", "-C, --clear");
940 printf(" %s\n", _("Clear thresholds"));
941 printf(" %s\n", "-E, --exact-match");
942 printf(" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
943 printf(" %s\n", "-e, --errors-only");
944 printf(" %s\n", _("Display only devices/mountpoints with errors"));
945 printf(" %s\n", "-f, --freespace-ignore-reserved");
946 printf(" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
947 printf(" %s\n", "-P, --iperfdata");
948 printf(" %s\n", _("Display inode usage in perfdata"));
949 printf(" %s\n", "-g, --group=NAME");
950 printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
951 printf(" %s\n", "-k, --kilobytes");
952 printf(" %s\n", _("Same as '--units kB'"));
953 printf(" %s\n", "-l, --local");
954 printf(" %s\n", _("Only check local filesystems"));
955 printf(" %s\n", "-L, --stat-remote-fs");
956 printf(" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
957 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
958 printf(" %s\n", "-M, --mountpoint");
959 printf(" %s\n", _("Display the (block) device instead of the mount point"));
960 printf(" %s\n", "-m, --megabytes");
961 printf(" %s\n", _("Same as '--units MB'"));
962 printf(" %s\n", "-A, --all");
963 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
964 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
965 printf(" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
966 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
967 printf(" %s\n", _("Regular expression for path or partition (may be repeated)"));
968 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
969 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
970 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
971 printf(" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
972 printf(" %s\n", "-n, --ignore-missing");
973 printf(" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
974 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
975 printf(UT_PLUG_TIMEOUT
, DEFAULT_SOCKET_TIMEOUT
);
976 printf(" %s\n", "-u, --units=STRING");
977 printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
979 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX");
980 printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
981 printf(" %s\n", "-N, --include-type=TYPE_REGEX");
982 printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
985 printf("%s\n", _("General usage hints:"));
986 printf(" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
987 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
988 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\""));
991 printf("%s\n", _("Examples:"));
992 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
993 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
994 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
995 printf(" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
996 printf(" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
997 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
998 printf(" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
1003 void print_usage(void) {
1004 printf("%s\n", _("Usage:"));
1005 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K "
1006 "inode_percentage_limit } {-p path | -x device}\n",
1008 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
1009 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
1012 bool stat_path(struct parameter_list
*p
) {
1013 /* Stat entry to check that dir exists and is accessible */
1015 printf("calling stat on %s\n", p
->name
);
1016 if (stat(p
->name
, &stat_buf
[0])) {
1018 printf("stat failed on %s\n", p
->name
);
1019 if (ignore_missing
== true) {
1022 printf("DISK %s - ", _("CRITICAL"));
1023 die(STATE_CRITICAL
, _("%s %s: %s\n"), p
->name
, _("is not accessible"), strerror(errno
));
1028 void get_stats(struct parameter_list
*p
, struct fs_usage
*fsp
) {
1029 struct parameter_list
*p_list
;
1030 struct fs_usage tmpfsp
;
1033 if (p
->group
== NULL
) {
1034 get_path_stats(p
, fsp
);
1036 /* find all group members */
1037 for (p_list
= path_select_list
; p_list
; p_list
= p_list
->name_next
) {
1039 if (strncmp(p_list
->name
, "/cygdrive/", 10) != 0)
1042 if (p_list
->group
&& !(strcmp(p_list
->group
, p
->group
))) {
1043 if (!stat_path(p_list
))
1045 get_fs_usage(p_list
->best_match
->me_mountdir
, p_list
->best_match
->me_devname
, &tmpfsp
);
1046 get_path_stats(p_list
, &tmpfsp
);
1048 printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n",
1049 p_list
->group
, tmpfsp
.fsu_blocks
, tmpfsp
.fsu_blocksize
, p_list
->best_match
->me_mountdir
, p_list
->dused_units
,
1050 p_list
->dfree_units
, p_list
->dtotal_units
, mult
);
1052 /* prevent counting the first FS of a group twice since its parameter_list entry
1053 * is used to carry the information of all file systems of the entire group */
1055 p
->total
+= p_list
->total
;
1056 p
->available
+= p_list
->available
;
1057 p
->available_to_root
+= p_list
->available_to_root
;
1058 p
->used
+= p_list
->used
;
1060 p
->dused_units
+= p_list
->dused_units
;
1061 p
->dfree_units
+= p_list
->dfree_units
;
1062 p
->dtotal_units
+= p_list
->dtotal_units
;
1063 p
->inodes_total
+= p_list
->inodes_total
;
1064 p
->inodes_free
+= p_list
->inodes_free
;
1065 p
->inodes_free_to_root
+= p_list
->inodes_free_to_root
;
1066 p
->inodes_used
+= p_list
->inodes_used
;
1071 printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p
->group
,
1072 p
->dused_units
, p
->dfree_units
, p
->dtotal_units
, tmpfsp
.fsu_blocksize
, mult
);
1074 /* modify devname and mountdir for output */
1075 p
->best_match
->me_mountdir
= p
->best_match
->me_devname
= p
->group
;
1077 /* finally calculate percentages for either plain FS or summed up group */
1078 p
->dused_pct
= calculate_percent(p
->used
, p
->used
+ p
->available
); /* used + available can never be > uintmax */
1079 p
->dfree_pct
= 100.0 - p
->dused_pct
;
1080 p
->dused_inodes_percent
= calculate_percent(p
->inodes_total
- p
->inodes_free
, p
->inodes_total
);
1081 p
->dfree_inodes_percent
= 100 - p
->dused_inodes_percent
;
1084 void get_path_stats(struct parameter_list
*p
, struct fs_usage
*fsp
) {
1085 p
->available
= fsp
->fsu_bavail
;
1086 p
->available_to_root
= fsp
->fsu_bfree
;
1087 p
->used
= fsp
->fsu_blocks
- fsp
->fsu_bfree
;
1088 if (freespace_ignore_reserved
) {
1089 /* option activated : we subtract the root-reserved space from the total */
1090 p
->total
= fsp
->fsu_blocks
- p
->available_to_root
+ p
->available
;
1092 /* default behaviour : take all the blocks into account */
1093 p
->total
= fsp
->fsu_blocks
;
1096 p
->dused_units
= p
->used
* fsp
->fsu_blocksize
/ mult
;
1097 p
->dfree_units
= p
->available
* fsp
->fsu_blocksize
/ mult
;
1098 p
->dtotal_units
= p
->total
* fsp
->fsu_blocksize
/ mult
;
1099 /* Free file nodes. Not sure the workaround is required, but in case...*/
1100 p
->inodes_free
= fsp
->fsu_ffree
;
1101 p
->inodes_free_to_root
= fsp
->fsu_ffree
; /* Free file nodes for root. */
1102 p
->inodes_used
= fsp
->fsu_files
- fsp
->fsu_ffree
;
1103 if (freespace_ignore_reserved
) {
1104 /* option activated : we subtract the root-reserved inodes from the total */
1105 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1106 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1107 p
->inodes_total
= fsp
->fsu_files
- p
->inodes_free_to_root
+ p
->inodes_free
;
1109 /* default behaviour : take all the inodes into account */
1110 p
->inodes_total
= fsp
->fsu_files
;
1112 np_add_name(&seen
, p
->best_match
->me_mountdir
);