Die on SSL initialization errors
[monitoring-plugins.git] / plugins / check_disk.c
blob04d588fb74a8d4694c4513ae4d6b205d148c2d89
1 /*****************************************************************************
2 *
3 * Nagios check_disk plugin
4 *
5 * License: GPL
6 * Copyright (c) 1999-2008 Nagios Plugins Development Team
7 *
8 * Description:
9 *
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-2008";
32 const char *email = "nagiosplug-devel@lists.sourceforge.net";
35 #include "common.h"
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
38 #endif
39 #if HAVE_INTTYPES_H
40 # include <inttypes.h>
41 #endif
42 #include <assert.h>
43 #include "popen.h"
44 #include "utils.h"
45 #include "utils_disk.h"
46 #include <stdarg.h>
47 #include "fsusage.h"
48 #include "mountlist.h"
49 #include "intprops.h" /* necessary for TYPE_MAXIMUM */
50 #if HAVE_LIMITS_H
51 # include <limits.h>
52 #endif
53 #include "regex.h"
56 /* If nonzero, show inode information. */
57 static int inode_format = 1;
59 /* If nonzero, show even filesystems with zero size or
60 uninteresting types. */
61 static int show_all_fs = 1;
63 /* If nonzero, show only local filesystems. */
64 static int show_local_fs = 0;
66 /* If nonzero, show only local filesystems but call stat() on remote ones. */
67 static int stat_remote_fs = 0;
69 /* If positive, the units to use when printing sizes;
70 if negative, the human-readable base. */
71 /* static int output_block_size; */
73 /* If nonzero, invoke the `sync' system call before getting any usage data.
74 Using this option can make df very slow, especially with many or very
75 busy disks. Note that this may make a difference on some systems --
76 SunOs4.1.3, for one. It is *not* necessary on Linux. */
77 /* static int require_sync = 0; */
79 /* Linked list of filesystem types to display.
80 If `fs_select_list' is NULL, list all types.
81 This table is generated dynamically from command-line options,
82 rather than hardcoding into the program what it thinks are the
83 valid filesystem types; let the user specify any filesystem type
84 they want to, and if there are any filesystems of that type, they
85 will be shown.
87 Some filesystem types:
88 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
90 /* static struct parameter_list *fs_select_list; */
92 /* Linked list of filesystem types to omit.
93 If the list is empty, don't exclude any types. */
94 static struct name_list *fs_exclude_list;
96 /* Linked list of filesystem types to check.
97 If the list is empty, include all types. */
98 static struct name_list *fs_include_list;
100 static struct name_list *dp_exclude_list;
102 static struct parameter_list *path_select_list = NULL;
104 /* Linked list of mounted filesystems. */
105 static struct mount_entry *mount_list;
107 /* For long options that have no equivalent short option, use a
108 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
109 enum
111 SYNC_OPTION = CHAR_MAX + 1,
112 NO_SYNC_OPTION,
113 BLOCK_SIZE_OPTION
116 #ifdef _AIX
117 #pragma alloca
118 #endif
120 int process_arguments (int, char **);
121 void print_path (const char *mypath);
122 void set_all_thresholds (struct parameter_list *path);
123 int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *);
124 void print_help (void);
125 void print_usage (void);
126 double calculate_percent(uintmax_t, uintmax_t);
127 void stat_path (struct parameter_list *p);
128 void get_stats (struct parameter_list *p, struct fs_usage *fsp);
129 void get_path_stats (struct parameter_list *p, struct fs_usage *fsp);
131 double w_dfp = -1.0;
132 double c_dfp = -1.0;
133 char *path;
134 char *exclude_device;
135 char *units;
136 uintmax_t mult = 1024 * 1024;
137 int verbose = 0;
138 int erronly = FALSE;
139 int display_mntp = FALSE;
140 int exact_match = FALSE;
141 int freespace_ignore_reserved = FALSE;
142 char *warn_freespace_units = NULL;
143 char *crit_freespace_units = NULL;
144 char *warn_freespace_percent = NULL;
145 char *crit_freespace_percent = NULL;
146 char *warn_usedspace_units = NULL;
147 char *crit_usedspace_units = NULL;
148 char *warn_usedspace_percent = NULL;
149 char *crit_usedspace_percent = NULL;
150 char *warn_usedinodes_percent = NULL;
151 char *crit_usedinodes_percent = NULL;
152 char *warn_freeinodes_percent = NULL;
153 char *crit_freeinodes_percent = NULL;
154 int path_selected = FALSE;
155 char *group = NULL;
156 struct stat *stat_buf;
157 struct name_list *seen = NULL;
161 main (int argc, char **argv)
163 int result = STATE_UNKNOWN;
164 int disk_result = STATE_UNKNOWN;
165 char *output;
166 char *details;
167 char *perf;
168 char *preamble;
169 double inode_space_pct;
170 double warning_high_tide;
171 double critical_high_tide;
172 int temp_result;
174 struct mount_entry *me;
175 struct fs_usage fsp, tmpfsp;
176 struct parameter_list *temp_list, *path;
178 preamble = strdup (" - free space:");
179 output = strdup ("");
180 details = strdup ("");
181 perf = strdup ("");
182 stat_buf = malloc(sizeof *stat_buf);
184 setlocale (LC_ALL, "");
185 bindtextdomain (PACKAGE, LOCALEDIR);
186 textdomain (PACKAGE);
188 mount_list = read_file_system_list (0);
190 /* Parse extra opts if any */
191 argv = np_extra_opts (&argc, argv, progname);
193 if (process_arguments (argc, argv) == ERROR)
194 usage4 (_("Could not parse arguments"));
196 /* If a list of paths has not been selected, find entire
197 mount list and create list of paths
199 if (path_selected == FALSE) {
200 for (me = mount_list; me; me = me->me_next) {
201 if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
202 path = np_add_parameter(&path_select_list, me->me_mountdir);
204 path->best_match = me;
205 path->group = group;
206 set_all_thresholds(path);
209 np_set_best_match(path_select_list, mount_list, exact_match);
211 /* Error if no match found for specified paths */
212 temp_list = path_select_list;
214 while (temp_list) {
215 if (! temp_list->best_match) {
216 die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), temp_list->name);
219 temp_list = temp_list->name_next;
222 /* Process for every path in list */
223 for (path = path_select_list; path; path=path->name_next) {
225 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
226 printf("Thresholds(pct) for %s warn: %f crit %f\n",path->name, path->freespace_percent->warning->end,
227 path->freespace_percent->critical->end);
229 if (verbose >= 3 && path->group != NULL)
230 printf("Group of %s: %s\n",path->name,path->group);
232 /* reset disk result */
233 disk_result = STATE_UNKNOWN;
235 me = path->best_match;
237 /* Filters */
239 /* Remove filesystems already seen */
240 if (np_seen_name(seen, me->me_mountdir)) {
241 continue;
243 np_add_name(&seen, me->me_mountdir);
245 if (path->group == NULL) {
246 /* Skip remote filesystems if we're not interested in them */
247 if (me->me_remote && show_local_fs) {
248 if (stat_remote_fs)
249 stat_path(path);
250 continue;
251 /* Skip pseudo fs's if we haven't asked for all fs's */
252 } else if (me->me_dummy && !show_all_fs) {
253 continue;
254 /* Skip excluded fstypes */
255 } else if (fs_exclude_list && np_find_name (fs_exclude_list, me->me_type)) {
256 continue;
257 /* Skip excluded fs's */
258 } else if (dp_exclude_list &&
259 (np_find_name (dp_exclude_list, me->me_devname) ||
260 np_find_name (dp_exclude_list, me->me_mountdir))) {
261 continue;
262 /* Skip not included fstypes */
263 } else if (fs_include_list && !np_find_name (fs_include_list, me->me_type)) {
264 continue;
268 stat_path(path);
269 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
271 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
272 get_stats (path, &fsp);
274 if (verbose >= 3) {
275 printf ("For %s, used_pct=%g free_pct=%g used_units=%g free_units=%g total_units=%g used_inodes_pct=%g free_inodes_pct=%g fsp.fsu_blocksize=%llu mult=%llu\n",
276 me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units, path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult);
279 /* Threshold comparisons */
281 temp_result = get_status(path->dfree_units, path->freespace_units);
282 if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
283 disk_result = max_state( disk_result, temp_result );
285 temp_result = get_status(path->dfree_pct, path->freespace_percent);
286 if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
287 disk_result = max_state( disk_result, temp_result );
289 temp_result = get_status(path->dused_units, path->usedspace_units);
290 if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
291 disk_result = max_state( disk_result, temp_result );
293 temp_result = get_status(path->dused_pct, path->usedspace_percent);
294 if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
295 disk_result = max_state( disk_result, temp_result );
297 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
298 if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
299 disk_result = max_state( disk_result, temp_result );
301 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
302 if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
303 disk_result = max_state( disk_result, temp_result );
305 result = max_state(result, disk_result);
307 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
308 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
309 data. Assumption that start=0. Roll on new syntax...
312 /* *_high_tide must be reinitialized at each run */
313 warning_high_tide = UINT_MAX;
314 critical_high_tide = UINT_MAX;
316 if (path->freespace_units->warning != NULL) {
317 warning_high_tide = path->dtotal_units - path->freespace_units->warning->end;
319 if (path->freespace_percent->warning != NULL) {
320 warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freespace_percent->warning->end/100)*path->dtotal_units ));
322 if (path->freespace_units->critical != NULL) {
323 critical_high_tide = path->dtotal_units - path->freespace_units->critical->end;
325 if (path->freespace_percent->critical != NULL) {
326 critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freespace_percent->critical->end/100)*path->dtotal_units ));
329 /* Nb: *_high_tide are unset when == UINT_MAX */
330 xasprintf (&perf, "%s %s", perf,
331 perfdata ((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
332 path->dused_units, units,
333 (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide,
334 (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide,
335 TRUE, 0,
336 TRUE, path->dtotal_units));
338 if (disk_result==STATE_OK && erronly && !verbose)
339 continue;
341 xasprintf (&output, "%s %s %.0f %s (%.0f%%",
342 output,
343 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
344 path->dfree_units,
345 units,
346 path->dfree_pct);
347 if (path->dused_inodes_percent < 0) {
348 xasprintf(&output, "%s inode=-);", output);
349 } else {
350 xasprintf(&output, "%s inode=%.0f%%);", output, path->dfree_inodes_percent );
353 /* TODO: Need to do a similar debug line
354 xasprintf (&details, _("%s\n\
355 %.0f of %.0f %s (%.0f%% inode=%.0f%%) free on %s (type %s mounted on %s) warn:%lu crit:%lu warn%%:%.0f%% crit%%:%.0f%%"),
356 details, dfree_units, dtotal_units, units, dfree_pct, inode_space_pct,
357 me->me_devname, me->me_type, me->me_mountdir,
358 (unsigned long)w_df, (unsigned long)c_df, w_dfp, c_dfp);
365 if (verbose >= 2)
366 xasprintf (&output, "%s%s", output, details);
369 printf ("DISK %s%s%s|%s\n", state_text (result), (erronly && result==STATE_OK) ? "" : preamble, output, perf);
370 return result;
374 double calculate_percent(uintmax_t value, uintmax_t total) {
375 double pct = -1;
376 /* I don't understand the below, but it is taken from coreutils' df */
377 /* Seems to be calculating pct, in the best possible way */
378 if (value <= TYPE_MAXIMUM(uintmax_t) / 100
379 && total != 0) {
380 uintmax_t u100 = value * 100;
381 pct = u100 / total + (u100 % total != 0);
382 } else {
383 /* Possible rounding errors - see coreutils' df for more explanation */
384 double u = value;
385 double t = total;
386 if (t) {
387 long int lipct = pct = u * 100 / t;
388 double ipct = lipct;
390 /* Like 'pct = ceil (dpct);', but without ceil - from coreutils again */
391 if (ipct - 1 < pct && pct <= ipct + 1)
392 pct = ipct + (ipct < pct);
395 return pct;
398 /* process command-line arguments */
400 process_arguments (int argc, char **argv)
402 int c, err;
403 struct parameter_list *se;
404 struct parameter_list *temp_list = NULL, *previous = NULL;
405 struct parameter_list *temp_path_select_list = NULL;
406 struct mount_entry *me, *temp_me;
407 int result = OK;
408 regex_t re;
409 int cflags = REG_NOSUB | REG_EXTENDED;
410 int default_cflags = cflags;
411 char errbuf[MAX_INPUT_BUFFER];
412 int fnd = 0;
414 int option = 0;
415 static struct option longopts[] = {
416 {"timeout", required_argument, 0, 't'},
417 {"warning", required_argument, 0, 'w'},
418 {"critical", required_argument, 0, 'c'},
419 {"iwarning", required_argument, 0, 'W'},
420 /* Dang, -C is taken. We might want to reshuffle this. */
421 {"icritical", required_argument, 0, 'K'},
422 {"kilobytes", no_argument, 0, 'k'},
423 {"megabytes", no_argument, 0, 'm'},
424 {"units", required_argument, 0, 'u'},
425 {"path", required_argument, 0, 'p'},
426 {"partition", required_argument, 0, 'p'},
427 {"exclude_device", required_argument, 0, 'x'},
428 {"exclude-type", required_argument, 0, 'X'},
429 {"include-type", required_argument, 0, 'N'},
430 {"group", required_argument, 0, 'g'},
431 {"eregi-path", required_argument, 0, 'R'},
432 {"eregi-partition", required_argument, 0, 'R'},
433 {"ereg-path", required_argument, 0, 'r'},
434 {"ereg-partition", required_argument, 0, 'r'},
435 {"freespace-ignore-reserved", no_argument, 0, 'f'},
436 {"ignore-ereg-path", required_argument, 0, 'i'},
437 {"ignore-ereg-partition", required_argument, 0, 'i'},
438 {"ignore-eregi-path", required_argument, 0, 'I'},
439 {"ignore-eregi-partition", required_argument, 0, 'I'},
440 {"local", no_argument, 0, 'l'},
441 {"stat-remote-fs", no_argument, 0, 'L'},
442 {"mountpoint", no_argument, 0, 'M'},
443 {"errors-only", no_argument, 0, 'e'},
444 {"exact-match", no_argument, 0, 'E'},
445 {"all", no_argument, 0, 'A'},
446 {"verbose", no_argument, 0, 'v'},
447 {"quiet", no_argument, 0, 'q'},
448 {"clear", no_argument, 0, 'C'},
449 {"version", no_argument, 0, 'V'},
450 {"help", no_argument, 0, 'h'},
451 {0, 0, 0, 0}
454 if (argc < 2)
455 return ERROR;
457 np_add_name(&fs_exclude_list, "iso9660");
459 for (c = 1; c < argc; c++)
460 if (strcmp ("-to", argv[c]) == 0)
461 strcpy (argv[c], "-t");
463 while (1) {
464 c = getopt_long (argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLg:R:r:i:I:MEA", longopts, &option);
466 if (c == -1 || c == EOF)
467 break;
469 switch (c) {
470 case 't': /* timeout period */
471 if (is_integer (optarg)) {
472 timeout_interval = atoi (optarg);
473 break;
475 else {
476 usage2 (_("Timeout interval must be a positive integer"), optarg);
479 /* See comments for 'c' */
480 case 'w': /* warning threshold */
481 if (strstr(optarg, "%")) {
482 if (*optarg == '@') {
483 warn_freespace_percent = optarg;
484 } else {
485 xasprintf(&warn_freespace_percent, "@%s", optarg);
487 } else {
488 if (*optarg == '@') {
489 warn_freespace_units = optarg;
490 } else {
491 xasprintf(&warn_freespace_units, "@%s", optarg);
494 break;
496 /* Awful mistake where the range values do not make sense. Normally,
497 you alert if the value is within the range, but since we are using
498 freespace, we have to alert if outside the range. Thus we artifically
499 force @ at the beginning of the range, so that it is backwards compatible
501 case 'c': /* critical threshold */
502 if (strstr(optarg, "%")) {
503 if (*optarg == '@') {
504 crit_freespace_percent = optarg;
505 } else {
506 xasprintf(&crit_freespace_percent, "@%s", optarg);
508 } else {
509 if (*optarg == '@') {
510 crit_freespace_units = optarg;
511 } else {
512 xasprintf(&crit_freespace_units, "@%s", optarg);
515 break;
517 case 'W': /* warning inode threshold */
518 if (*optarg == '@') {
519 warn_freeinodes_percent = optarg;
520 } else {
521 xasprintf(&warn_freeinodes_percent, "@%s", optarg);
523 break;
524 case 'K': /* critical inode threshold */
525 if (*optarg == '@') {
526 crit_freeinodes_percent = optarg;
527 } else {
528 xasprintf(&crit_freeinodes_percent, "@%s", optarg);
530 break;
531 case 'u':
532 if (units)
533 free(units);
534 if (! strcmp (optarg, "bytes")) {
535 mult = (uintmax_t)1;
536 units = strdup ("B");
537 } else if (! strcmp (optarg, "kB")) {
538 mult = (uintmax_t)1024;
539 units = strdup ("kB");
540 } else if (! strcmp (optarg, "MB")) {
541 mult = (uintmax_t)1024 * 1024;
542 units = strdup ("MB");
543 } else if (! strcmp (optarg, "GB")) {
544 mult = (uintmax_t)1024 * 1024 * 1024;
545 units = strdup ("GB");
546 } else if (! strcmp (optarg, "TB")) {
547 mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
548 units = strdup ("TB");
549 } else {
550 die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
552 if (units == NULL)
553 die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
554 break;
555 case 'k': /* display mountpoint */
556 mult = 1024;
557 if (units)
558 free(units);
559 units = strdup ("kB");
560 break;
561 case 'm': /* display mountpoint */
562 mult = 1024 * 1024;
563 if (units)
564 free(units);
565 units = strdup ("MB");
566 break;
567 case 'L':
568 stat_remote_fs = 1;
569 case 'l':
570 show_local_fs = 1;
571 break;
572 case 'p': /* select path */
573 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
574 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
575 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
576 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
577 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
580 /* add parameter if not found. overwrite thresholds if path has already been added */
581 if (! (se = np_find_parameter(path_select_list, optarg))) {
582 se = np_add_parameter(&path_select_list, optarg);
584 se->group = group;
585 set_all_thresholds(se);
587 /* With autofs, it is required to stat() the path before re-populating the mount_list */
588 stat_path(se);
589 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
590 * pointers are copied around. One of the reason it wasn't done yet is that other parts
591 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
592 mount_list = read_file_system_list (0);
593 np_set_best_match(se, mount_list, exact_match);
595 path_selected = TRUE;
596 break;
597 case 'x': /* exclude path or partition */
598 np_add_name(&dp_exclude_list, optarg);
599 break;
600 case 'X': /* exclude file system type */
601 np_add_name(&fs_exclude_list, optarg);
602 break;
603 case 'N': /* include file system type */
604 np_add_name(&fs_include_list, optarg);
605 break;
606 case 'v': /* verbose */
607 verbose++;
608 break;
609 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
610 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
611 erronly = TRUE;
612 break;
613 case 'e':
614 erronly = TRUE;
615 break;
616 case 'E':
617 if (path_selected)
618 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
619 exact_match = TRUE;
620 break;
621 case 'f':
622 freespace_ignore_reserved = TRUE;
623 break;
624 case 'g':
625 if (path_selected)
626 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
627 group = optarg;
628 break;
629 case 'I':
630 cflags |= REG_ICASE;
631 case 'i':
632 if (!path_selected)
633 die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
634 err = regcomp(&re, optarg, cflags);
635 if (err != 0) {
636 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
637 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
640 temp_list = path_select_list;
642 previous = NULL;
643 while (temp_list) {
644 if (temp_list->best_match) {
645 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
647 if (verbose >=3)
648 printf("ignoring %s matching regex\n", temp_list->name);
650 temp_list = np_del_parameter(temp_list, previous);
651 /* pointer to first element needs to be updated if first item gets deleted */
652 if (previous == NULL)
653 path_select_list = temp_list;
654 } else {
655 previous = temp_list;
656 temp_list = temp_list->name_next;
658 } else {
659 previous = temp_list;
660 temp_list = temp_list->name_next;
665 cflags = default_cflags;
666 break;
668 case 'A':
669 optarg = strdup(".*");
670 case 'R':
671 cflags |= REG_ICASE;
672 case 'r':
673 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
674 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
675 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
676 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
677 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n"));
680 err = regcomp(&re, optarg, cflags);
681 if (err != 0) {
682 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
683 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
686 for (me = mount_list; me; me = me->me_next) {
687 if (np_regex_match_mount_entry(me, &re)) {
688 fnd = TRUE;
689 if (verbose >= 3)
690 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
692 /* add parameter if not found. overwrite thresholds if path has already been added */
693 if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
694 se = np_add_parameter(&path_select_list, me->me_mountdir);
696 se->group = group;
697 set_all_thresholds(se);
701 if (!fnd)
702 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
703 _("Regular expression did not match any path or disk"), optarg);
705 fnd = FALSE;
706 path_selected = TRUE;
707 np_set_best_match(path_select_list, mount_list, exact_match);
708 cflags = default_cflags;
710 break;
711 case 'M': /* display mountpoint */
712 display_mntp = TRUE;
713 break;
714 case 'C':
715 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
716 if (path_selected == FALSE) {
717 struct parameter_list *path;
718 for (me = mount_list; me; me = me->me_next) {
719 if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
720 path = np_add_parameter(&path_select_list, me->me_mountdir);
721 path->best_match = me;
722 path->group = group;
723 set_all_thresholds(path);
726 warn_freespace_units = NULL;
727 crit_freespace_units = NULL;
728 warn_usedspace_units = NULL;
729 crit_usedspace_units = NULL;
730 warn_freespace_percent = NULL;
731 crit_freespace_percent = NULL;
732 warn_usedspace_percent = NULL;
733 crit_usedspace_percent = NULL;
734 warn_usedinodes_percent = NULL;
735 crit_usedinodes_percent = NULL;
736 warn_freeinodes_percent = NULL;
737 crit_freeinodes_percent = NULL;
739 path_selected = FALSE;
740 group = NULL;
741 break;
742 case 'V': /* version */
743 print_revision (progname, NP_VERSION);
744 exit (STATE_OK);
745 case 'h': /* help */
746 print_help ();
747 exit (STATE_OK);
748 case '?': /* help */
749 usage (_("Unknown argument"));
753 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
754 c = optind;
755 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
756 warn_usedspace_percent = argv[c++];
758 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
759 crit_usedspace_percent = argv[c++];
761 if (argc > c && path == NULL) {
762 se = np_add_parameter(&path_select_list, strdup(argv[c++]));
763 path_selected = TRUE;
764 set_all_thresholds(se);
767 if (units == NULL) {
768 units = strdup ("MB");
769 mult = (uintmax_t)1024 * 1024;
772 return TRUE;
777 void
778 print_path (const char *mypath)
780 if (mypath == NULL)
781 printf ("\n");
782 else
783 printf (_(" for %s\n"), mypath);
787 void
788 set_all_thresholds (struct parameter_list *path)
790 if (path->freespace_units != NULL) free(path->freespace_units);
791 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
792 if (path->freespace_percent != NULL) free (path->freespace_percent);
793 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
794 if (path->usedspace_units != NULL) free (path->usedspace_units);
795 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
796 if (path->usedspace_percent != NULL) free (path->usedspace_percent);
797 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
798 if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
799 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
800 if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
801 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
804 /* TODO: Remove?
807 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
809 if (w < 0 && c < 0 && wp < 0.0 && cp < 0.0) {
810 printf (_("INPUT ERROR: No thresholds specified"));
811 print_path (mypath);
812 return ERROR;
814 else if ((wp >= 0.0 || cp >= 0.0) &&
815 (wp < 0.0 || cp < 0.0 || wp > 100.0 || cp > 100.0 || cp > wp)) {
816 printf (_("\
817 INPUT ERROR: C_DFP (%f) should be less than W_DFP (%.1f) and both should be between zero and 100 percent, inclusive"),
818 cp, wp);
819 print_path (mypath);
820 return ERROR;
822 else if ((iwp >= 0.0 || icp >= 0.0) &&
823 (iwp < 0.0 || icp < 0.0 || iwp > 100.0 || icp > 100.0 || icp > iwp)) {
824 printf (_("\
825 INPUT ERROR: C_IDFP (%f) should be less than W_IDFP (%.1f) and both should be between zero and 100 percent, inclusive"),
826 icp, iwp);
827 print_path (mypath);
828 return ERROR;
830 else if ((w > 0 || c > 0) && (w == 0 || c == 0 || c > w)) {
831 printf (_("\
832 INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greater than zero"),
833 (unsigned long)c, (unsigned long)w);
834 print_path (mypath);
835 return ERROR;
838 return OK;
849 void
850 print_help (void)
852 print_revision (progname, NP_VERSION);
854 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
855 printf (COPYRIGHT, copyright, email);
857 printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
858 printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
860 printf ("\n\n");
862 print_usage ();
864 printf (UT_HELP_VRSN);
865 printf (UT_EXTRA_OPTS);
867 printf (" %s\n", "-w, --warning=INTEGER");
868 printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
869 printf (" %s\n", "-w, --warning=PERCENT%");
870 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
871 printf (" %s\n", "-c, --critical=INTEGER");
872 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
873 printf (" %s\n", "-c, --critical=PERCENT%");
874 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
875 printf (" %s\n", "-W, --iwarning=PERCENT%");
876 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
877 printf (" %s\n", "-K, --icritical=PERCENT%");
878 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
879 printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
880 printf (" %s\n", _("Path or partition (may be repeated)"));
881 printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
882 printf (" %s\n", _("Ignore device (only works if -p unspecified)"));
883 printf (" %s\n", "-C, --clear");
884 printf (" %s\n", _("Clear thresholds"));
885 printf (" %s\n", "-E, --exact-match");
886 printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
887 printf (" %s\n", "-e, --errors-only");
888 printf (" %s\n", _("Display only devices/mountpoints with errors"));
889 printf (" %s\n", "-f, --freespace-ignore-reserved");
890 printf (" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
891 printf (" %s\n", "-g, --group=NAME");
892 printf (" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
893 printf (" %s\n", "-k, --kilobytes");
894 printf (" %s\n", _("Same as '--units kB'"));
895 printf (" %s\n", "-l, --local");
896 printf (" %s\n", _("Only check local filesystems"));
897 printf (" %s\n", "-L, --stat-remote-fs");
898 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
899 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
900 printf (" %s\n", "-M, --mountpoint");
901 printf (" %s\n", _("Display the mountpoint instead of the partition"));
902 printf (" %s\n", "-m, --megabytes");
903 printf (" %s\n", _("Same as '--units MB'"));
904 printf (" %s\n", "-A, --all");
905 printf (" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
906 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
907 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
908 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
909 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
910 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
911 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
912 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
913 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
914 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
915 printf (" %s\n", "-u, --units=STRING");
916 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
917 printf (UT_VERBOSE);
918 printf (" %s\n", "-X, --exclude-type=TYPE");
919 printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
920 printf (" %s\n", "-N, --include-type=TYPE");
921 printf (" %s\n", _("Check only filesystems of indicated type (may be repeated)"));
923 printf ("\n");
924 printf ("%s\n", _("Examples:"));
925 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
926 printf (" %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
927 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
928 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
929 printf (" %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
930 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
931 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
933 printf (UT_SUPPORT);
938 void
939 print_usage (void)
941 printf ("%s\n", _("Usage:"));
942 printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
943 printf ("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
944 printf ("[-t timeout] [-u unit] [-v] [-X type] [-N type]\n");
947 void
948 stat_path (struct parameter_list *p)
950 /* Stat entry to check that dir exists and is accessible */
951 if (verbose >= 3)
952 printf("calling stat on %s\n", p->name);
953 if (stat (p->name, &stat_buf[0])) {
954 if (verbose >= 3)
955 printf("stat failed on %s\n", p->name);
956 printf("DISK %s - ", _("CRITICAL"));
957 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
962 void
963 get_stats (struct parameter_list *p, struct fs_usage *fsp) {
964 struct parameter_list *p_list;
965 struct fs_usage tmpfsp;
966 int first = 1;
968 if (p->group == NULL) {
969 get_path_stats(p,fsp);
970 } else {
971 /* find all group members */
972 for (p_list = path_select_list; p_list; p_list=p_list->name_next) {
973 if (p_list->group && ! (strcmp(p_list->group, p->group))) {
974 stat_path(p_list);
975 get_fs_usage (p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
976 get_path_stats(p_list, &tmpfsp);
977 if (verbose >= 3)
978 printf("Group %s: adding %llu blocks sized %llu, (%s) used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
979 p_list->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, p_list->dfree_units,
980 p_list->dtotal_units, mult);
982 /* prevent counting the first FS of a group twice since its parameter_list entry
983 * is used to carry the information of all file systems of the entire group */
984 if (! first) {
985 p->total += p_list->total;
986 p->available += p_list->available;
987 p->available_to_root += p_list->available_to_root;
988 p->used += p_list->used;
990 p->dused_units += p_list->dused_units;
991 p->dfree_units += p_list->dfree_units;
992 p->dtotal_units += p_list->dtotal_units;
993 p->inodes_total += p_list->inodes_total;
994 p->inodes_free += p_list->inodes_free;
996 first = 0;
998 if (verbose >= 3)
999 printf("Group %s now has: used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
1000 p->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p->best_match->me_mountdir, p->dused_units,
1001 p->dfree_units, p->dtotal_units, mult);
1003 /* modify devname and mountdir for output */
1004 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1006 /* finally calculate percentages for either plain FS or summed up group */
1007 p->dused_pct = calculate_percent( p->used, p->used + p->available ); /* used + available can never be > uintmax */
1008 p->dfree_pct = 100 - p->dused_pct;
1009 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1010 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1014 void
1015 get_path_stats (struct parameter_list *p, struct fs_usage *fsp) {
1016 /* 2007-12-08 - Workaround for Gnulib reporting insanely high available
1017 * space on BSD (the actual value should be negative but fsp->fsu_bavail
1018 * is unsigned) */
1019 p->available = fsp->fsu_bavail > fsp->fsu_bfree ? 0 : fsp->fsu_bavail;
1020 p->available_to_root = fsp->fsu_bfree;
1021 p->used = fsp->fsu_blocks - fsp->fsu_bfree;
1022 if (freespace_ignore_reserved) {
1023 /* option activated : we substract the root-reserved space from the total */
1024 p->total = fsp->fsu_blocks - p->available_to_root + p->available;
1025 } else {
1026 /* default behaviour : take all the blocks into account */
1027 p->total = fsp->fsu_blocks;
1030 p->dused_units = p->used*fsp->fsu_blocksize/mult;
1031 p->dfree_units = p->available*fsp->fsu_blocksize/mult;
1032 p->dtotal_units = p->total*fsp->fsu_blocksize/mult;
1033 p->inodes_total = fsp->fsu_files; /* Total file nodes. */
1034 p->inodes_free = fsp->fsu_ffree; /* Free file nodes. */
1035 np_add_name(&seen, p->best_match->me_mountdir);