Merge branch 'master' of https://github.com/cern-mig/nagios-plugins
[monitoring-plugins.git] / plugins / check_disk.c
blob4ea0393509821c772f7f748b66da58e952a51d05
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 char *warn_freespace_units = NULL;
142 char *crit_freespace_units = NULL;
143 char *warn_freespace_percent = NULL;
144 char *crit_freespace_percent = NULL;
145 char *warn_usedspace_units = NULL;
146 char *crit_usedspace_units = NULL;
147 char *warn_usedspace_percent = NULL;
148 char *crit_usedspace_percent = NULL;
149 char *warn_usedinodes_percent = NULL;
150 char *crit_usedinodes_percent = NULL;
151 char *warn_freeinodes_percent = NULL;
152 char *crit_freeinodes_percent = NULL;
153 int path_selected = FALSE;
154 char *group = NULL;
155 struct stat *stat_buf;
156 struct name_list *seen = NULL;
160 main (int argc, char **argv)
162 int result = STATE_UNKNOWN;
163 int disk_result = STATE_UNKNOWN;
164 char *output;
165 char *details;
166 char *perf;
167 char *preamble;
168 double inode_space_pct;
169 double warning_high_tide;
170 double critical_high_tide;
171 int temp_result;
173 struct mount_entry *me;
174 struct fs_usage fsp, tmpfsp;
175 struct parameter_list *temp_list, *path;
177 preamble = strdup (" - free space:");
178 output = strdup ("");
179 details = strdup ("");
180 perf = strdup ("");
181 stat_buf = malloc(sizeof *stat_buf);
183 setlocale (LC_ALL, "");
184 bindtextdomain (PACKAGE, LOCALEDIR);
185 textdomain (PACKAGE);
187 mount_list = read_file_system_list (0);
189 /* Parse extra opts if any */
190 argv = np_extra_opts (&argc, argv, progname);
192 if (process_arguments (argc, argv) == ERROR)
193 usage4 (_("Could not parse arguments"));
195 /* If a list of paths has not been selected, find entire
196 mount list and create list of paths
198 if (path_selected == FALSE) {
199 for (me = mount_list; me; me = me->me_next) {
200 if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
201 path = np_add_parameter(&path_select_list, me->me_mountdir);
203 path->best_match = me;
204 path->group = group;
205 set_all_thresholds(path);
208 np_set_best_match(path_select_list, mount_list, exact_match);
210 /* Error if no match found for specified paths */
211 temp_list = path_select_list;
213 while (temp_list) {
214 if (! temp_list->best_match) {
215 die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), temp_list->name);
218 temp_list = temp_list->name_next;
221 /* Process for every path in list */
222 for (path = path_select_list; path; path=path->name_next) {
224 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
225 printf("Thresholds(pct) for %s warn: %f crit %f\n",path->name, path->freespace_percent->warning->end,
226 path->freespace_percent->critical->end);
228 if (verbose >= 3 && path->group != NULL)
229 printf("Group of %s: %s\n",path->name,path->group);
231 /* reset disk result */
232 disk_result = STATE_UNKNOWN;
234 me = path->best_match;
236 /* Filters */
238 /* Remove filesystems already seen */
239 if (np_seen_name(seen, me->me_mountdir)) {
240 continue;
242 np_add_name(&seen, me->me_mountdir);
244 if (path->group == NULL) {
245 /* Skip remote filesystems if we're not interested in them */
246 if (me->me_remote && show_local_fs) {
247 if (stat_remote_fs)
248 stat_path(path);
249 continue;
250 /* Skip pseudo fs's if we haven't asked for all fs's */
251 } else if (me->me_dummy && !show_all_fs) {
252 continue;
253 /* Skip excluded fstypes */
254 } else if (fs_exclude_list && np_find_name (fs_exclude_list, me->me_type)) {
255 continue;
256 /* Skip excluded fs's */
257 } else if (dp_exclude_list &&
258 (np_find_name (dp_exclude_list, me->me_devname) ||
259 np_find_name (dp_exclude_list, me->me_mountdir))) {
260 continue;
261 /* Skip not included fstypes */
262 } else if (fs_include_list && !np_find_name (fs_include_list, me->me_type)) {
263 continue;
266 stat_path(path);
267 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
270 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
271 get_stats (path, &fsp);
273 if (verbose >= 3) {
274 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",
275 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);
278 /* Threshold comparisons */
280 temp_result = get_status(path->dfree_units, path->freespace_units);
281 if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
282 disk_result = max_state( disk_result, temp_result );
284 temp_result = get_status(path->dfree_pct, path->freespace_percent);
285 if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
286 disk_result = max_state( disk_result, temp_result );
288 temp_result = get_status(path->dused_units, path->usedspace_units);
289 if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
290 disk_result = max_state( disk_result, temp_result );
292 temp_result = get_status(path->dused_pct, path->usedspace_percent);
293 if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
294 disk_result = max_state( disk_result, temp_result );
296 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
297 if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
298 disk_result = max_state( disk_result, temp_result );
300 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
301 if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
302 disk_result = max_state( disk_result, temp_result );
304 result = max_state(result, disk_result);
306 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
307 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
308 data. Assumption that start=0. Roll on new syntax...
311 /* *_high_tide must be reinitialized at each run */
312 warning_high_tide = UINT_MAX;
313 critical_high_tide = UINT_MAX;
315 if (path->freespace_units->warning != NULL) {
316 warning_high_tide = path->dtotal_units - path->freespace_units->warning->end;
318 if (path->freespace_percent->warning != NULL) {
319 warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freespace_percent->warning->end/100)*path->dtotal_units ));
321 if (path->freespace_units->critical != NULL) {
322 critical_high_tide = path->dtotal_units - path->freespace_units->critical->end;
324 if (path->freespace_percent->critical != NULL) {
325 critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freespace_percent->critical->end/100)*path->dtotal_units ));
328 /* Nb: *_high_tide are unset when == UINT_MAX */
329 xasprintf (&perf, "%s %s", perf,
330 perfdata ((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
331 path->dused_units, units,
332 (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide,
333 (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide,
334 TRUE, 0,
335 TRUE, path->dtotal_units));
337 if (disk_result==STATE_OK && erronly && !verbose)
338 continue;
340 xasprintf (&output, "%s %s %.0f %s (%.0f%%",
341 output,
342 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
343 path->dfree_units,
344 units,
345 path->dfree_pct);
346 if (path->dused_inodes_percent < 0) {
347 xasprintf(&output, "%s inode=-);", output);
348 } else {
349 xasprintf(&output, "%s inode=%.0f%%);", output, path->dfree_inodes_percent );
352 /* TODO: Need to do a similar debug line
353 xasprintf (&details, _("%s\n\
354 %.0f of %.0f %s (%.0f%% inode=%.0f%%) free on %s (type %s mounted on %s) warn:%lu crit:%lu warn%%:%.0f%% crit%%:%.0f%%"),
355 details, dfree_units, dtotal_units, units, dfree_pct, inode_space_pct,
356 me->me_devname, me->me_type, me->me_mountdir,
357 (unsigned long)w_df, (unsigned long)c_df, w_dfp, c_dfp);
364 if (verbose >= 2)
365 xasprintf (&output, "%s%s", output, details);
368 printf ("DISK %s%s%s|%s\n", state_text (result), (erronly && result==STATE_OK) ? "" : preamble, output, perf);
369 return result;
373 double calculate_percent(uintmax_t value, uintmax_t total) {
374 double pct = -1;
375 /* I don't understand the below, but it is taken from coreutils' df */
376 /* Seems to be calculating pct, in the best possible way */
377 if (value <= TYPE_MAXIMUM(uintmax_t) / 100
378 && total != 0) {
379 uintmax_t u100 = value * 100;
380 pct = u100 / total + (u100 % total != 0);
381 } else {
382 /* Possible rounding errors - see coreutils' df for more explanation */
383 double u = value;
384 double t = total;
385 if (t) {
386 long int lipct = pct = u * 100 / t;
387 double ipct = lipct;
389 /* Like 'pct = ceil (dpct);', but without ceil - from coreutils again */
390 if (ipct - 1 < pct && pct <= ipct + 1)
391 pct = ipct + (ipct < pct);
394 return pct;
397 /* process command-line arguments */
399 process_arguments (int argc, char **argv)
401 int c, err;
402 struct parameter_list *se;
403 struct parameter_list *temp_list = NULL, *previous = NULL;
404 struct parameter_list *temp_path_select_list = NULL;
405 struct mount_entry *me, *temp_me;
406 int result = OK;
407 regex_t re;
408 int cflags = REG_NOSUB | REG_EXTENDED;
409 int default_cflags = cflags;
410 char errbuf[MAX_INPUT_BUFFER];
411 int fnd = 0;
413 int option = 0;
414 static struct option longopts[] = {
415 {"timeout", required_argument, 0, 't'},
416 {"warning", required_argument, 0, 'w'},
417 {"critical", required_argument, 0, 'c'},
418 {"iwarning", required_argument, 0, 'W'},
419 /* Dang, -C is taken. We might want to reshuffle this. */
420 {"icritical", required_argument, 0, 'K'},
421 {"kilobytes", no_argument, 0, 'k'},
422 {"megabytes", no_argument, 0, 'm'},
423 {"units", required_argument, 0, 'u'},
424 {"path", required_argument, 0, 'p'},
425 {"partition", required_argument, 0, 'p'},
426 {"exclude_device", required_argument, 0, 'x'},
427 {"exclude-type", required_argument, 0, 'X'},
428 {"include-type", required_argument, 0, 'N'},
429 {"group", required_argument, 0, 'g'},
430 {"eregi-path", required_argument, 0, 'R'},
431 {"eregi-partition", required_argument, 0, 'R'},
432 {"ereg-path", required_argument, 0, 'r'},
433 {"ereg-partition", required_argument, 0, 'r'},
434 {"ignore-ereg-path", required_argument, 0, 'i'},
435 {"ignore-ereg-partition", required_argument, 0, 'i'},
436 {"ignore-eregi-path", required_argument, 0, 'I'},
437 {"ignore-eregi-partition", required_argument, 0, 'I'},
438 {"local", no_argument, 0, 'l'},
439 {"stat-remote-fs", no_argument, 0, 'L'},
440 {"mountpoint", no_argument, 0, 'M'},
441 {"errors-only", no_argument, 0, 'e'},
442 {"exact-match", no_argument, 0, 'E'},
443 {"all", no_argument, 0, 'A'},
444 {"verbose", no_argument, 0, 'v'},
445 {"quiet", no_argument, 0, 'q'},
446 {"clear", no_argument, 0, 'C'},
447 {"version", no_argument, 0, 'V'},
448 {"help", no_argument, 0, 'h'},
449 {0, 0, 0, 0}
452 if (argc < 2)
453 return ERROR;
455 np_add_name(&fs_exclude_list, "iso9660");
457 for (c = 1; c < argc; c++)
458 if (strcmp ("-to", argv[c]) == 0)
459 strcpy (argv[c], "-t");
461 while (1) {
462 c = getopt_long (argc, argv, "+?VqhveCt:c:w:K:W:u:p:x:X:N:mklLg:R:r:i:I:MEA", longopts, &option);
464 if (c == -1 || c == EOF)
465 break;
467 switch (c) {
468 case 't': /* timeout period */
469 if (is_integer (optarg)) {
470 timeout_interval = atoi (optarg);
471 break;
473 else {
474 usage2 (_("Timeout interval must be a positive integer"), optarg);
477 /* See comments for 'c' */
478 case 'w': /* warning threshold */
479 if (strstr(optarg, "%")) {
480 if (*optarg == '@') {
481 warn_freespace_percent = optarg;
482 } else {
483 xasprintf(&warn_freespace_percent, "@%s", optarg);
485 } else {
486 if (*optarg == '@') {
487 warn_freespace_units = optarg;
488 } else {
489 xasprintf(&warn_freespace_units, "@%s", optarg);
492 break;
494 /* Awful mistake where the range values do not make sense. Normally,
495 you alert if the value is within the range, but since we are using
496 freespace, we have to alert if outside the range. Thus we artifically
497 force @ at the beginning of the range, so that it is backwards compatible
499 case 'c': /* critical threshold */
500 if (strstr(optarg, "%")) {
501 if (*optarg == '@') {
502 crit_freespace_percent = optarg;
503 } else {
504 xasprintf(&crit_freespace_percent, "@%s", optarg);
506 } else {
507 if (*optarg == '@') {
508 crit_freespace_units = optarg;
509 } else {
510 xasprintf(&crit_freespace_units, "@%s", optarg);
513 break;
515 case 'W': /* warning inode threshold */
516 if (*optarg == '@') {
517 warn_freeinodes_percent = optarg;
518 } else {
519 xasprintf(&warn_freeinodes_percent, "@%s", optarg);
521 break;
522 case 'K': /* critical inode threshold */
523 if (*optarg == '@') {
524 crit_freeinodes_percent = optarg;
525 } else {
526 xasprintf(&crit_freeinodes_percent, "@%s", optarg);
528 break;
529 case 'u':
530 if (units)
531 free(units);
532 if (! strcmp (optarg, "bytes")) {
533 mult = (uintmax_t)1;
534 units = strdup ("B");
535 } else if (! strcmp (optarg, "kB")) {
536 mult = (uintmax_t)1024;
537 units = strdup ("kB");
538 } else if (! strcmp (optarg, "MB")) {
539 mult = (uintmax_t)1024 * 1024;
540 units = strdup ("MB");
541 } else if (! strcmp (optarg, "GB")) {
542 mult = (uintmax_t)1024 * 1024 * 1024;
543 units = strdup ("GB");
544 } else if (! strcmp (optarg, "TB")) {
545 mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
546 units = strdup ("TB");
547 } else {
548 die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
550 if (units == NULL)
551 die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
552 break;
553 case 'k': /* display mountpoint */
554 mult = 1024;
555 if (units)
556 free(units);
557 units = strdup ("kB");
558 break;
559 case 'm': /* display mountpoint */
560 mult = 1024 * 1024;
561 if (units)
562 free(units);
563 units = strdup ("MB");
564 break;
565 case 'L':
566 stat_remote_fs = 1;
567 case 'l':
568 show_local_fs = 1;
569 break;
570 case 'p': /* select path */
571 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
572 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
573 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
574 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
575 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
578 /* add parameter if not found. overwrite thresholds if path has already been added */
579 if (! (se = np_find_parameter(path_select_list, optarg))) {
580 se = np_add_parameter(&path_select_list, optarg);
582 se->group = group;
583 set_all_thresholds(se);
585 /* With autofs, it is required to stat() the path before re-populating the mount_list */
586 stat_path(se);
587 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
588 * pointers are copied around. One of the reason it wasn't done yet is that other parts
589 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
590 mount_list = read_file_system_list (0);
591 np_set_best_match(se, mount_list, exact_match);
593 path_selected = TRUE;
594 break;
595 case 'x': /* exclude path or partition */
596 np_add_name(&dp_exclude_list, optarg);
597 break;
598 case 'X': /* exclude file system type */
599 np_add_name(&fs_exclude_list, optarg);
600 break;
601 case 'N': /* include file system type */
602 np_add_name(&fs_include_list, optarg);
603 break;
604 case 'v': /* verbose */
605 verbose++;
606 break;
607 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
608 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
609 erronly = TRUE;
610 break;
611 case 'e':
612 erronly = TRUE;
613 break;
614 case 'E':
615 if (path_selected)
616 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
617 exact_match = TRUE;
618 break;
619 case 'g':
620 if (path_selected)
621 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
622 group = optarg;
623 break;
624 case 'I':
625 cflags |= REG_ICASE;
626 case 'i':
627 if (!path_selected)
628 die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
629 err = regcomp(&re, optarg, cflags);
630 if (err != 0) {
631 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
632 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
635 temp_list = path_select_list;
637 previous = NULL;
638 while (temp_list) {
639 if (temp_list->best_match) {
640 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
642 if (verbose >=3)
643 printf("ignoring %s matching regex\n", temp_list->name);
645 temp_list = np_del_parameter(temp_list, previous);
646 /* pointer to first element needs to be updated if first item gets deleted */
647 if (previous == NULL)
648 path_select_list = temp_list;
649 } else {
650 previous = temp_list;
651 temp_list = temp_list->name_next;
653 } else {
654 previous = temp_list;
655 temp_list = temp_list->name_next;
660 cflags = default_cflags;
661 break;
663 case 'A':
664 optarg = strdup(".*");
665 case 'R':
666 cflags |= REG_ICASE;
667 case 'r':
668 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
669 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
670 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
671 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
672 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n"));
675 err = regcomp(&re, optarg, cflags);
676 if (err != 0) {
677 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
678 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
681 for (me = mount_list; me; me = me->me_next) {
682 if (np_regex_match_mount_entry(me, &re)) {
683 fnd = TRUE;
684 if (verbose >= 3)
685 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
687 /* add parameter if not found. overwrite thresholds if path has already been added */
688 if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
689 se = np_add_parameter(&path_select_list, me->me_mountdir);
691 se->group = group;
692 set_all_thresholds(se);
696 if (!fnd)
697 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
698 _("Regular expression did not match any path or disk"), optarg);
700 fnd = FALSE;
701 path_selected = TRUE;
702 np_set_best_match(path_select_list, mount_list, exact_match);
703 cflags = default_cflags;
705 break;
706 case 'M': /* display mountpoint */
707 display_mntp = TRUE;
708 break;
709 case 'C':
710 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
711 if (path_selected == FALSE) {
712 struct parameter_list *path;
713 for (me = mount_list; me; me = me->me_next) {
714 if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
715 path = np_add_parameter(&path_select_list, me->me_mountdir);
716 path->best_match = me;
717 path->group = group;
718 set_all_thresholds(path);
721 warn_freespace_units = NULL;
722 crit_freespace_units = NULL;
723 warn_usedspace_units = NULL;
724 crit_usedspace_units = NULL;
725 warn_freespace_percent = NULL;
726 crit_freespace_percent = NULL;
727 warn_usedspace_percent = NULL;
728 crit_usedspace_percent = NULL;
729 warn_usedinodes_percent = NULL;
730 crit_usedinodes_percent = NULL;
731 warn_freeinodes_percent = NULL;
732 crit_freeinodes_percent = NULL;
734 path_selected = FALSE;
735 group = NULL;
736 break;
737 case 'V': /* version */
738 print_revision (progname, NP_VERSION);
739 exit (STATE_OK);
740 case 'h': /* help */
741 print_help ();
742 exit (STATE_OK);
743 case '?': /* help */
744 usage (_("Unknown argument"));
748 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
749 c = optind;
750 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
751 warn_usedspace_percent = argv[c++];
753 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
754 crit_usedspace_percent = argv[c++];
756 if (argc > c && path == NULL) {
757 se = np_add_parameter(&path_select_list, strdup(argv[c++]));
758 path_selected = TRUE;
759 set_all_thresholds(se);
762 if (units == NULL) {
763 units = strdup ("MB");
764 mult = (uintmax_t)1024 * 1024;
767 return TRUE;
772 void
773 print_path (const char *mypath)
775 if (mypath == NULL)
776 printf ("\n");
777 else
778 printf (_(" for %s\n"), mypath);
782 void
783 set_all_thresholds (struct parameter_list *path)
785 if (path->freespace_units != NULL) free(path->freespace_units);
786 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
787 if (path->freespace_percent != NULL) free (path->freespace_percent);
788 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
789 if (path->usedspace_units != NULL) free (path->usedspace_units);
790 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
791 if (path->usedspace_percent != NULL) free (path->usedspace_percent);
792 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
793 if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
794 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
795 if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
796 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
799 /* TODO: Remove?
802 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
804 if (w < 0 && c < 0 && wp < 0.0 && cp < 0.0) {
805 printf (_("INPUT ERROR: No thresholds specified"));
806 print_path (mypath);
807 return ERROR;
809 else if ((wp >= 0.0 || cp >= 0.0) &&
810 (wp < 0.0 || cp < 0.0 || wp > 100.0 || cp > 100.0 || cp > wp)) {
811 printf (_("\
812 INPUT ERROR: C_DFP (%f) should be less than W_DFP (%.1f) and both should be between zero and 100 percent, inclusive"),
813 cp, wp);
814 print_path (mypath);
815 return ERROR;
817 else if ((iwp >= 0.0 || icp >= 0.0) &&
818 (iwp < 0.0 || icp < 0.0 || iwp > 100.0 || icp > 100.0 || icp > iwp)) {
819 printf (_("\
820 INPUT ERROR: C_IDFP (%f) should be less than W_IDFP (%.1f) and both should be between zero and 100 percent, inclusive"),
821 icp, iwp);
822 print_path (mypath);
823 return ERROR;
825 else if ((w > 0 || c > 0) && (w == 0 || c == 0 || c > w)) {
826 printf (_("\
827 INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greater than zero"),
828 (unsigned long)c, (unsigned long)w);
829 print_path (mypath);
830 return ERROR;
833 return OK;
844 void
845 print_help (void)
847 print_revision (progname, NP_VERSION);
849 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
850 printf (COPYRIGHT, copyright, email);
852 printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
853 printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
855 printf ("\n\n");
857 print_usage ();
859 printf (UT_HELP_VRSN);
860 printf (UT_EXTRA_OPTS);
862 printf (" %s\n", "-w, --warning=INTEGER");
863 printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
864 printf (" %s\n", "-w, --warning=PERCENT%");
865 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
866 printf (" %s\n", "-c, --critical=INTEGER");
867 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
868 printf (" %s\n", "-c, --critical=PERCENT%");
869 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
870 printf (" %s\n", "-W, --iwarning=PERCENT%");
871 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
872 printf (" %s\n", "-K, --icritical=PERCENT%");
873 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
874 printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
875 printf (" %s\n", _("Path or partition (may be repeated)"));
876 printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
877 printf (" %s\n", _("Ignore device (only works if -p unspecified)"));
878 printf (" %s\n", "-C, --clear");
879 printf (" %s\n", _("Clear thresholds"));
880 printf (" %s\n", "-E, --exact-match");
881 printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
882 printf (" %s\n", "-e, --errors-only");
883 printf (" %s\n", _("Display only devices/mountpoints with errors"));
884 printf (" %s\n", "-g, --group=NAME");
885 printf (" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
886 printf (" %s\n", "-k, --kilobytes");
887 printf (" %s\n", _("Same as '--units kB'"));
888 printf (" %s\n", "-l, --local");
889 printf (" %s\n", _("Only check local filesystems"));
890 printf (" %s\n", "-L, --stat-remote-fs");
891 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
892 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
893 printf (" %s\n", "-M, --mountpoint");
894 printf (" %s\n", _("Display the mountpoint instead of the partition"));
895 printf (" %s\n", "-m, --megabytes");
896 printf (" %s\n", _("Same as '--units MB'"));
897 printf (" %s\n", "-A, --all");
898 printf (" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
899 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
900 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
901 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
902 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
903 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
904 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
905 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
906 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
907 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
908 printf (" %s\n", "-u, --units=STRING");
909 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
910 printf (UT_VERBOSE);
911 printf (" %s\n", "-X, --exclude-type=TYPE");
912 printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
913 printf (" %s\n", "-N, --include-type=TYPE");
914 printf (" %s\n", _("Check only filesystems of indicated type (may be repeated)"));
916 printf ("\n");
917 printf ("%s\n", _("Examples:"));
918 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
919 printf (" %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
920 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
921 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
922 printf (" %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
923 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
924 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
926 printf (UT_SUPPORT);
931 void
932 print_usage (void)
934 printf ("%s\n", _("Usage:"));
935 printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
936 printf ("[-C] [-E] [-e] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
937 printf ("[-t timeout] [-u unit] [-v] [-X type] [-N type]\n");
940 void
941 stat_path (struct parameter_list *p)
943 /* Stat entry to check that dir exists and is accessible */
944 if (verbose >= 3)
945 printf("calling stat on %s\n", p->name);
946 if (stat (p->name, &stat_buf[0])) {
947 if (verbose >= 3)
948 printf("stat failed on %s\n", p->name);
949 printf("DISK %s - ", _("CRITICAL"));
950 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
955 void
956 get_stats (struct parameter_list *p, struct fs_usage *fsp) {
957 struct parameter_list *p_list;
958 struct fs_usage tmpfsp;
959 int first = 1;
961 if (p->group == NULL) {
962 get_path_stats(p,fsp);
963 } else {
964 /* find all group members */
965 for (p_list = path_select_list; p_list; p_list=p_list->name_next) {
966 if (p_list->group && ! (strcmp(p_list->group, p->group))) {
967 stat_path(p_list);
968 get_fs_usage (p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
969 get_path_stats(p_list, &tmpfsp);
970 if (verbose >= 3)
971 printf("Group %s: adding %llu blocks sized %llu, (%s) used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
972 p_list->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, p_list->dfree_units,
973 p_list->dtotal_units, mult);
975 /* prevent counting the first FS of a group twice since its parameter_list entry
976 * is used to carry the information of all file systems of the entire group */
977 if (! first) {
978 p->total += p_list->total;
979 p->available += p_list->available;
980 p->available_to_root += p_list->available_to_root;
981 p->used += p_list->used;
983 p->dused_units += p_list->dused_units;
984 p->dfree_units += p_list->dfree_units;
985 p->dtotal_units += p_list->dtotal_units;
986 p->inodes_total += p_list->inodes_total;
987 p->inodes_free += p_list->inodes_free;
989 first = 0;
991 if (verbose >= 3)
992 printf("Group %s now has: used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
993 p->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p->best_match->me_mountdir, p->dused_units,
994 p->dfree_units, p->dtotal_units, mult);
996 /* modify devname and mountdir for output */
997 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
999 /* finally calculate percentages for either plain FS or summed up group */
1000 p->dused_pct = calculate_percent( p->used, p->used + p->available ); /* used + available can never be > uintmax */
1001 p->dfree_pct = 100 - p->dused_pct;
1002 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1003 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1007 void
1008 get_path_stats (struct parameter_list *p, struct fs_usage *fsp) {
1009 p->total = fsp->fsu_blocks;
1010 /* 2007-12-08 - Workaround for Gnulib reporting insanely high available
1011 * space on BSD (the actual value should be negative but fsp->fsu_bavail
1012 * is unsigned) */
1013 p->available = fsp->fsu_bavail > fsp->fsu_bfree ? 0 : fsp->fsu_bavail;
1014 p->available_to_root = fsp->fsu_bfree;
1015 p->used = p->total - p->available_to_root;
1017 p->dused_units = p->used*fsp->fsu_blocksize/mult;
1018 p->dfree_units = p->available*fsp->fsu_blocksize/mult;
1019 p->dtotal_units = p->total*fsp->fsu_blocksize/mult;
1020 p->inodes_total = fsp->fsu_files; /* Total file nodes. */
1021 p->inodes_free = fsp->fsu_ffree; /* Free file nodes. */
1022 np_add_name(&seen, p->best_match->me_mountdir);