Merge pull request #1586 from computersalat/master
[monitoring-plugins.git] / plugins / check_disk.c
blob1c43e8543d001abdf611d6b591ef5c1394fadb5e
1 /*****************************************************************************
2 *
3 * Monitoring check_disk plugin
4 *
5 * License: GPL
6 * Copyright (c) 1999-2008 Monitoring 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 = "devel@monitoring-plugins.org";
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"
55 #ifdef __CYGWIN__
56 # include <windows.h>
57 # undef ERROR
58 # define ERROR -1
59 #endif
61 /* If nonzero, show even filesystems with zero size or
62 uninteresting types. */
63 static int show_all_fs = 1;
65 /* If nonzero, show only local filesystems. */
66 static int show_local_fs = 0;
68 /* If nonzero, show only local filesystems but call stat() on remote ones. */
69 static int stat_remote_fs = 0;
71 /* If positive, the units to use when printing sizes;
72 if negative, the human-readable base. */
73 /* static int output_block_size; */
75 /* If nonzero, invoke the `sync' system call before getting any usage data.
76 Using this option can make df very slow, especially with many or very
77 busy disks. Note that this may make a difference on some systems --
78 SunOs4.1.3, for one. It is *not* necessary on Linux. */
79 /* static int require_sync = 0; */
81 /* Linked list of filesystem types to display.
82 If `fs_select_list' is NULL, list all types.
83 This table is generated dynamically from command-line options,
84 rather than hardcoding into the program what it thinks are the
85 valid filesystem types; let the user specify any filesystem type
86 they want to, and if there are any filesystems of that type, they
87 will be shown.
89 Some filesystem types:
90 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
92 /* static struct parameter_list *fs_select_list; */
94 /* Linked list of filesystem types to omit.
95 If the list is empty, don't exclude any types. */
96 static struct name_list *fs_exclude_list;
98 /* Linked list of filesystem types to check.
99 If the list is empty, include all types. */
100 static struct name_list *fs_include_list;
102 static struct name_list *dp_exclude_list;
104 static struct parameter_list *path_select_list = NULL;
106 /* Linked list of mounted filesystems. */
107 static struct mount_entry *mount_list;
109 /* For long options that have no equivalent short option, use a
110 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
111 enum
113 SYNC_OPTION = CHAR_MAX + 1,
114 NO_SYNC_OPTION,
115 BLOCK_SIZE_OPTION
118 #ifdef _AIX
119 #pragma alloca
120 #endif
122 int process_arguments (int, char **);
123 void print_path (const char *mypath);
124 void set_all_thresholds (struct parameter_list *path);
125 int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *);
126 void print_help (void);
127 void print_usage (void);
128 double calculate_percent(uintmax_t, uintmax_t);
129 void stat_path (struct parameter_list *p);
130 void get_stats (struct parameter_list *p, struct fs_usage *fsp);
131 void get_path_stats (struct parameter_list *p, struct fs_usage *fsp);
133 double w_dfp = -1.0;
134 double c_dfp = -1.0;
135 char *path;
136 char *exclude_device;
137 char *units;
138 uintmax_t mult = 1024 * 1024;
139 int verbose = 0;
140 int erronly = FALSE;
141 int display_mntp = FALSE;
142 int exact_match = FALSE;
143 int freespace_ignore_reserved = FALSE;
144 int display_inodes_perfdata = FALSE;
145 char *warn_freespace_units = NULL;
146 char *crit_freespace_units = NULL;
147 char *warn_freespace_percent = NULL;
148 char *crit_freespace_percent = NULL;
149 char *warn_usedspace_units = NULL;
150 char *crit_usedspace_units = NULL;
151 char *warn_usedspace_percent = NULL;
152 char *crit_usedspace_percent = NULL;
153 char *warn_usedinodes_percent = NULL;
154 char *crit_usedinodes_percent = NULL;
155 char *warn_freeinodes_percent = NULL;
156 char *crit_freeinodes_percent = NULL;
157 int path_selected = FALSE;
158 char *group = NULL;
159 struct stat *stat_buf;
160 struct name_list *seen = NULL;
164 main (int argc, char **argv)
166 int result = STATE_UNKNOWN;
167 int disk_result = STATE_UNKNOWN;
168 char *output;
169 char *details;
170 char *perf;
171 char *perf_ilabel;
172 char *preamble;
173 char *flag_header;
174 double inode_space_pct;
175 double warning_high_tide;
176 double critical_high_tide;
177 int temp_result;
179 struct mount_entry *me;
180 struct fs_usage fsp;
181 struct parameter_list *temp_list, *path;
183 #ifdef __CYGWIN__
184 char mountdir[32];
185 #endif
187 preamble = strdup (" - free space:");
188 output = strdup ("");
189 details = strdup ("");
190 perf = strdup ("");
191 perf_ilabel = strdup ("");
192 stat_buf = malloc(sizeof *stat_buf);
194 setlocale (LC_ALL, "");
195 bindtextdomain (PACKAGE, LOCALEDIR);
196 textdomain (PACKAGE);
198 mount_list = read_file_system_list (0);
200 /* Parse extra opts if any */
201 argv = np_extra_opts (&argc, argv, progname);
203 if (process_arguments (argc, argv) == ERROR)
204 usage4 (_("Could not parse arguments"));
206 /* If a list of paths has not been selected, find entire
207 mount list and create list of paths
209 if (path_selected == FALSE) {
210 for (me = mount_list; me; me = me->me_next) {
211 if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
212 path = np_add_parameter(&path_select_list, me->me_mountdir);
214 path->best_match = me;
215 path->group = group;
216 set_all_thresholds(path);
219 np_set_best_match(path_select_list, mount_list, exact_match);
221 /* Error if no match found for specified paths */
222 temp_list = path_select_list;
224 while (temp_list) {
225 if (! temp_list->best_match) {
226 die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), temp_list->name);
229 temp_list = temp_list->name_next;
232 /* Process for every path in list */
233 for (path = path_select_list; path; path=path->name_next) {
234 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
235 printf("Thresholds(pct) for %s warn: %f crit %f\n",path->name, path->freespace_percent->warning->end,
236 path->freespace_percent->critical->end);
238 if (verbose >= 3 && path->group != NULL)
239 printf("Group of %s: %s\n",path->name,path->group);
241 /* reset disk result */
242 disk_result = STATE_UNKNOWN;
244 me = path->best_match;
246 #ifdef __CYGWIN__
247 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11)
248 continue;
249 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10);
250 if (GetDriveType(mountdir) != DRIVE_FIXED)
251 me->me_remote = 1;
252 #endif
253 /* Filters */
255 /* Remove filesystems already seen */
256 if (np_seen_name(seen, me->me_mountdir)) {
257 continue;
259 np_add_name(&seen, me->me_mountdir);
261 if (path->group == NULL) {
262 /* Skip remote filesystems if we're not interested in them */
263 if (me->me_remote && show_local_fs) {
264 if (stat_remote_fs)
265 stat_path(path);
266 continue;
267 /* Skip pseudo fs's if we haven't asked for all fs's */
268 } else if (me->me_dummy && !show_all_fs) {
269 continue;
270 /* Skip excluded fstypes */
271 } else if (fs_exclude_list && np_find_name (fs_exclude_list, me->me_type)) {
272 continue;
273 /* Skip excluded fs's */
274 } else if (dp_exclude_list &&
275 (np_find_name (dp_exclude_list, me->me_devname) ||
276 np_find_name (dp_exclude_list, me->me_mountdir))) {
277 continue;
278 /* Skip not included fstypes */
279 } else if (fs_include_list && !np_find_name (fs_include_list, me->me_type)) {
280 continue;
284 stat_path(path);
285 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
287 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
288 get_stats (path, &fsp);
290 if (verbose >= 3) {
291 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",
292 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);
295 /* Threshold comparisons */
297 temp_result = get_status(path->dfree_units, path->freespace_units);
298 if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
299 disk_result = max_state( disk_result, temp_result );
301 temp_result = get_status(path->dfree_pct, path->freespace_percent);
302 if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
303 disk_result = max_state( disk_result, temp_result );
305 temp_result = get_status(path->dused_units, path->usedspace_units);
306 if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
307 disk_result = max_state( disk_result, temp_result );
309 temp_result = get_status(path->dused_pct, path->usedspace_percent);
310 if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
311 disk_result = max_state( disk_result, temp_result );
313 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
314 if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
315 disk_result = max_state( disk_result, temp_result );
317 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
318 if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
319 disk_result = max_state( disk_result, temp_result );
321 result = max_state(result, disk_result);
323 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
324 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
325 data. Assumption that start=0. Roll on new syntax...
328 /* *_high_tide must be reinitialized at each run */
329 warning_high_tide = UINT_MAX;
330 critical_high_tide = UINT_MAX;
332 if (path->freespace_units->warning != NULL) {
333 warning_high_tide = path->dtotal_units - path->freespace_units->warning->end;
335 if (path->freespace_percent->warning != NULL) {
336 warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freespace_percent->warning->end/100)*path->dtotal_units ));
338 if (path->freespace_units->critical != NULL) {
339 critical_high_tide = path->dtotal_units - path->freespace_units->critical->end;
341 if (path->freespace_percent->critical != NULL) {
342 critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freespace_percent->critical->end/100)*path->dtotal_units ));
345 /* Nb: *_high_tide are unset when == UINT_MAX */
346 xasprintf (&perf, "%s %s", perf,
347 perfdata ((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
348 path->dused_units, units,
349 (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide,
350 (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide,
351 TRUE, 0,
352 TRUE, path->dtotal_units));
354 if (display_inodes_perfdata) {
355 /* *_high_tide must be reinitialized at each run */
356 warning_high_tide = UINT_MAX;
357 critical_high_tide = UINT_MAX;
359 if (path->freeinodes_percent->warning != NULL) {
360 warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freeinodes_percent->warning->end/100)*path->inodes_total ));
362 if (path->freeinodes_percent->critical != NULL) {
363 critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freeinodes_percent->critical->end/100)*path->inodes_total ));
366 xasprintf (&perf_ilabel, "%s (inodes)", (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
367 /* Nb: *_high_tide are unset when == UINT_MAX */
368 xasprintf (&perf, "%s %s", perf,
369 perfdata (perf_ilabel,
370 path->inodes_used, "",
371 (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide,
372 (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide,
373 TRUE, 0,
374 TRUE, path->inodes_total));
377 if (disk_result==STATE_OK && erronly && !verbose)
378 continue;
380 if(disk_result && verbose >= 1) {
381 xasprintf(&flag_header, " %s [", state_text (disk_result));
382 } else {
383 xasprintf(&flag_header, "");
385 xasprintf (&output, "%s%s %s %.0f %s (%.0f%%",
386 output, flag_header,
387 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
388 path->dfree_units,
389 units,
390 path->dfree_pct);
391 if (path->dused_inodes_percent < 0) {
392 xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : ""));
393 } else {
394 xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : ""));
396 free(flag_header);
397 /* TODO: Need to do a similar debug line
398 xasprintf (&details, _("%s\n\
399 %.0f of %.0f %s (%.0f%% inode=%.0f%%) free on %s (type %s mounted on %s) warn:%lu crit:%lu warn%%:%.0f%% crit%%:%.0f%%"),
400 details, dfree_units, dtotal_units, units, dfree_pct, inode_space_pct,
401 me->me_devname, me->me_type, me->me_mountdir,
402 (unsigned long)w_df, (unsigned long)c_df, w_dfp, c_dfp);
409 if (verbose >= 2)
410 xasprintf (&output, "%s%s", output, details);
413 printf ("DISK %s%s%s|%s\n", state_text (result), (erronly && result==STATE_OK) ? "" : preamble, output, perf);
414 return result;
418 double calculate_percent(uintmax_t value, uintmax_t total) {
419 double pct = -1;
420 /* I don't understand the below, but it is taken from coreutils' df */
421 /* Seems to be calculating pct, in the best possible way */
422 if (value <= TYPE_MAXIMUM(uintmax_t) / 100
423 && total != 0) {
424 uintmax_t u100 = value * 100;
425 pct = u100 / total + (u100 % total != 0);
426 } else {
427 /* Possible rounding errors - see coreutils' df for more explanation */
428 double u = value;
429 double t = total;
430 if (t) {
431 long int lipct = pct = u * 100 / t;
432 double ipct = lipct;
434 /* Like 'pct = ceil (dpct);', but without ceil - from coreutils again */
435 if (ipct - 1 < pct && pct <= ipct + 1)
436 pct = ipct + (ipct < pct);
439 return pct;
442 /* process command-line arguments */
444 process_arguments (int argc, char **argv)
446 int c, err;
447 struct parameter_list *se;
448 struct parameter_list *temp_list = NULL, *previous = NULL;
449 struct mount_entry *me;
450 regex_t re;
451 int cflags = REG_NOSUB | REG_EXTENDED;
452 int default_cflags = cflags;
453 char errbuf[MAX_INPUT_BUFFER];
454 int fnd = 0;
456 int option = 0;
457 static struct option longopts[] = {
458 {"timeout", required_argument, 0, 't'},
459 {"warning", required_argument, 0, 'w'},
460 {"critical", required_argument, 0, 'c'},
461 {"iwarning", required_argument, 0, 'W'},
462 /* Dang, -C is taken. We might want to reshuffle this. */
463 {"icritical", required_argument, 0, 'K'},
464 {"kilobytes", no_argument, 0, 'k'},
465 {"megabytes", no_argument, 0, 'm'},
466 {"units", required_argument, 0, 'u'},
467 {"path", required_argument, 0, 'p'},
468 {"partition", required_argument, 0, 'p'},
469 {"exclude_device", required_argument, 0, 'x'},
470 {"exclude-type", required_argument, 0, 'X'},
471 {"include-type", required_argument, 0, 'N'},
472 {"group", required_argument, 0, 'g'},
473 {"eregi-path", required_argument, 0, 'R'},
474 {"eregi-partition", required_argument, 0, 'R'},
475 {"ereg-path", required_argument, 0, 'r'},
476 {"ereg-partition", required_argument, 0, 'r'},
477 {"freespace-ignore-reserved", no_argument, 0, 'f'},
478 {"ignore-ereg-path", required_argument, 0, 'i'},
479 {"ignore-ereg-partition", required_argument, 0, 'i'},
480 {"ignore-eregi-path", required_argument, 0, 'I'},
481 {"ignore-eregi-partition", required_argument, 0, 'I'},
482 {"local", no_argument, 0, 'l'},
483 {"stat-remote-fs", no_argument, 0, 'L'},
484 {"iperfdata", no_argument, 0, 'P'},
485 {"mountpoint", no_argument, 0, 'M'},
486 {"errors-only", no_argument, 0, 'e'},
487 {"exact-match", no_argument, 0, 'E'},
488 {"all", no_argument, 0, 'A'},
489 {"verbose", no_argument, 0, 'v'},
490 {"quiet", no_argument, 0, 'q'},
491 {"clear", no_argument, 0, 'C'},
492 {"version", no_argument, 0, 'V'},
493 {"help", no_argument, 0, 'h'},
494 {0, 0, 0, 0}
497 if (argc < 2)
498 return ERROR;
500 np_add_name(&fs_exclude_list, "iso9660");
502 for (c = 1; c < argc; c++)
503 if (strcmp ("-to", argv[c]) == 0)
504 strcpy (argv[c], "-t");
506 while (1) {
507 c = getopt_long (argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEA", longopts, &option);
509 if (c == -1 || c == EOF)
510 break;
512 switch (c) {
513 case 't': /* timeout period */
514 if (is_integer (optarg)) {
515 timeout_interval = atoi (optarg);
516 break;
518 else {
519 usage2 (_("Timeout interval must be a positive integer"), optarg);
522 /* See comments for 'c' */
523 case 'w': /* warning threshold */
524 if (strstr(optarg, "%")) {
525 if (*optarg == '@') {
526 warn_freespace_percent = optarg;
527 } else {
528 xasprintf(&warn_freespace_percent, "@%s", optarg);
530 } else {
531 if (*optarg == '@') {
532 warn_freespace_units = optarg;
533 } else {
534 xasprintf(&warn_freespace_units, "@%s", optarg);
537 break;
539 /* Awful mistake where the range values do not make sense. Normally,
540 you alert if the value is within the range, but since we are using
541 freespace, we have to alert if outside the range. Thus we artifically
542 force @ at the beginning of the range, so that it is backwards compatible
544 case 'c': /* critical threshold */
545 if (strstr(optarg, "%")) {
546 if (*optarg == '@') {
547 crit_freespace_percent = optarg;
548 } else {
549 xasprintf(&crit_freespace_percent, "@%s", optarg);
551 } else {
552 if (*optarg == '@') {
553 crit_freespace_units = optarg;
554 } else {
555 xasprintf(&crit_freespace_units, "@%s", optarg);
558 break;
560 case 'W': /* warning inode threshold */
561 if (*optarg == '@') {
562 warn_freeinodes_percent = optarg;
563 } else {
564 xasprintf(&warn_freeinodes_percent, "@%s", optarg);
566 break;
567 case 'K': /* critical inode threshold */
568 if (*optarg == '@') {
569 crit_freeinodes_percent = optarg;
570 } else {
571 xasprintf(&crit_freeinodes_percent, "@%s", optarg);
573 break;
574 case 'u':
575 if (units)
576 free(units);
577 if (! strcmp (optarg, "bytes")) {
578 mult = (uintmax_t)1;
579 units = strdup ("B");
580 } else if (! strcmp (optarg, "kB")) {
581 mult = (uintmax_t)1024;
582 units = strdup ("kB");
583 } else if (! strcmp (optarg, "MB")) {
584 mult = (uintmax_t)1024 * 1024;
585 units = strdup ("MB");
586 } else if (! strcmp (optarg, "GB")) {
587 mult = (uintmax_t)1024 * 1024 * 1024;
588 units = strdup ("GB");
589 } else if (! strcmp (optarg, "TB")) {
590 mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
591 units = strdup ("TB");
592 } else {
593 die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
595 if (units == NULL)
596 die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
597 break;
598 case 'k': /* display mountpoint */
599 mult = 1024;
600 if (units)
601 free(units);
602 units = strdup ("kB");
603 break;
604 case 'm': /* display mountpoint */
605 mult = 1024 * 1024;
606 if (units)
607 free(units);
608 units = strdup ("MB");
609 break;
610 case 'L':
611 stat_remote_fs = 1;
612 /* fallthrough */
613 case 'l':
614 show_local_fs = 1;
615 break;
616 case 'P':
617 display_inodes_perfdata = 1;
618 break;
619 case 'p': /* select path */
620 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
621 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
622 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
623 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
624 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
627 /* add parameter if not found. overwrite thresholds if path has already been added */
628 if (! (se = np_find_parameter(path_select_list, optarg))) {
629 se = np_add_parameter(&path_select_list, optarg);
631 se->group = group;
632 set_all_thresholds(se);
634 /* With autofs, it is required to stat() the path before re-populating the mount_list */
635 stat_path(se);
636 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
637 * pointers are copied around. One of the reason it wasn't done yet is that other parts
638 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
639 mount_list = read_file_system_list (0);
640 np_set_best_match(se, mount_list, exact_match);
642 path_selected = TRUE;
643 break;
644 case 'x': /* exclude path or partition */
645 np_add_name(&dp_exclude_list, optarg);
646 break;
647 case 'X': /* exclude file system type */
648 np_add_name(&fs_exclude_list, optarg);
649 break;
650 case 'N': /* include file system type */
651 np_add_name(&fs_include_list, optarg);
652 break;
653 case 'v': /* verbose */
654 verbose++;
655 break;
656 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
657 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
658 erronly = TRUE;
659 break;
660 case 'e':
661 erronly = TRUE;
662 break;
663 case 'E':
664 if (path_selected)
665 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
666 exact_match = TRUE;
667 break;
668 case 'f':
669 freespace_ignore_reserved = TRUE;
670 break;
671 case 'g':
672 if (path_selected)
673 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
674 group = optarg;
675 break;
676 case 'I':
677 cflags |= REG_ICASE;
678 case 'i':
679 if (!path_selected)
680 die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
681 err = regcomp(&re, optarg, cflags);
682 if (err != 0) {
683 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
684 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
687 temp_list = path_select_list;
689 previous = NULL;
690 while (temp_list) {
691 if (temp_list->best_match) {
692 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
694 if (verbose >=3)
695 printf("ignoring %s matching regex\n", temp_list->name);
697 temp_list = np_del_parameter(temp_list, previous);
698 /* pointer to first element needs to be updated if first item gets deleted */
699 if (previous == NULL)
700 path_select_list = temp_list;
701 } else {
702 previous = temp_list;
703 temp_list = temp_list->name_next;
705 } else {
706 previous = temp_list;
707 temp_list = temp_list->name_next;
712 cflags = default_cflags;
713 break;
715 case 'A':
716 optarg = strdup(".*");
717 case 'R':
718 cflags |= REG_ICASE;
719 case 'r':
720 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
721 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
722 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
723 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
724 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n"));
727 err = regcomp(&re, optarg, cflags);
728 if (err != 0) {
729 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
730 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
733 for (me = mount_list; me; me = me->me_next) {
734 if (np_regex_match_mount_entry(me, &re)) {
735 fnd = TRUE;
736 if (verbose >= 3)
737 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
739 /* add parameter if not found. overwrite thresholds if path has already been added */
740 if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
741 se = np_add_parameter(&path_select_list, me->me_mountdir);
743 se->group = group;
744 set_all_thresholds(se);
748 if (!fnd)
749 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
750 _("Regular expression did not match any path or disk"), optarg);
752 fnd = FALSE;
753 path_selected = TRUE;
754 np_set_best_match(path_select_list, mount_list, exact_match);
755 cflags = default_cflags;
757 break;
758 case 'M': /* display mountpoint */
759 display_mntp = TRUE;
760 break;
761 case 'C':
762 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
763 if (path_selected == FALSE) {
764 struct parameter_list *path;
765 for (me = mount_list; me; me = me->me_next) {
766 if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
767 path = np_add_parameter(&path_select_list, me->me_mountdir);
768 path->best_match = me;
769 path->group = group;
770 set_all_thresholds(path);
773 warn_freespace_units = NULL;
774 crit_freespace_units = NULL;
775 warn_usedspace_units = NULL;
776 crit_usedspace_units = NULL;
777 warn_freespace_percent = NULL;
778 crit_freespace_percent = NULL;
779 warn_usedspace_percent = NULL;
780 crit_usedspace_percent = NULL;
781 warn_usedinodes_percent = NULL;
782 crit_usedinodes_percent = NULL;
783 warn_freeinodes_percent = NULL;
784 crit_freeinodes_percent = NULL;
786 path_selected = FALSE;
787 group = NULL;
788 break;
789 case 'V': /* version */
790 print_revision (progname, NP_VERSION);
791 exit (STATE_UNKNOWN);
792 case 'h': /* help */
793 print_help ();
794 exit (STATE_UNKNOWN);
795 case '?': /* help */
796 usage (_("Unknown argument"));
800 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
801 c = optind;
802 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
803 warn_usedspace_percent = argv[c++];
805 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
806 crit_usedspace_percent = argv[c++];
808 if (argc > c && path == NULL) {
809 se = np_add_parameter(&path_select_list, strdup(argv[c++]));
810 path_selected = TRUE;
811 set_all_thresholds(se);
814 if (units == NULL) {
815 units = strdup ("MB");
816 mult = (uintmax_t)1024 * 1024;
819 return TRUE;
824 void
825 print_path (const char *mypath)
827 if (mypath == NULL)
828 printf ("\n");
829 else
830 printf (_(" for %s\n"), mypath);
834 void
835 set_all_thresholds (struct parameter_list *path)
837 if (path->freespace_units != NULL) free(path->freespace_units);
838 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
839 if (path->freespace_percent != NULL) free (path->freespace_percent);
840 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
841 if (path->usedspace_units != NULL) free (path->usedspace_units);
842 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
843 if (path->usedspace_percent != NULL) free (path->usedspace_percent);
844 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
845 if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
846 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
847 if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
848 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
851 /* TODO: Remove?
854 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
856 if (w < 0 && c < 0 && wp < 0.0 && cp < 0.0) {
857 printf (_("INPUT ERROR: No thresholds specified"));
858 print_path (mypath);
859 return ERROR;
861 else if ((wp >= 0.0 || cp >= 0.0) &&
862 (wp < 0.0 || cp < 0.0 || wp > 100.0 || cp > 100.0 || cp > wp)) {
863 printf (_("\
864 INPUT ERROR: C_DFP (%f) should be less than W_DFP (%.1f) and both should be between zero and 100 percent, inclusive"),
865 cp, wp);
866 print_path (mypath);
867 return ERROR;
869 else if ((iwp >= 0.0 || icp >= 0.0) &&
870 (iwp < 0.0 || icp < 0.0 || iwp > 100.0 || icp > 100.0 || icp > iwp)) {
871 printf (_("\
872 INPUT ERROR: C_IDFP (%f) should be less than W_IDFP (%.1f) and both should be between zero and 100 percent, inclusive"),
873 icp, iwp);
874 print_path (mypath);
875 return ERROR;
877 else if ((w > 0 || c > 0) && (w == 0 || c == 0 || c > w)) {
878 printf (_("\
879 INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greater than zero"),
880 (unsigned long)c, (unsigned long)w);
881 print_path (mypath);
882 return ERROR;
885 return OK;
896 void
897 print_help (void)
899 print_revision (progname, NP_VERSION);
901 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
902 printf (COPYRIGHT, copyright, email);
904 printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
905 printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
907 printf ("\n\n");
909 print_usage ();
911 printf (UT_HELP_VRSN);
912 printf (UT_EXTRA_OPTS);
914 printf (" %s\n", "-w, --warning=INTEGER");
915 printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
916 printf (" %s\n", "-w, --warning=PERCENT%");
917 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
918 printf (" %s\n", "-c, --critical=INTEGER");
919 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
920 printf (" %s\n", "-c, --critical=PERCENT%");
921 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of disk space is free"));
922 printf (" %s\n", "-W, --iwarning=PERCENT%");
923 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
924 printf (" %s\n", "-K, --icritical=PERCENT%");
925 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
926 printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
927 printf (" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
928 printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
929 printf (" %s\n", _("Ignore device (only works if -p unspecified)"));
930 printf (" %s\n", "-C, --clear");
931 printf (" %s\n", _("Clear thresholds"));
932 printf (" %s\n", "-E, --exact-match");
933 printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
934 printf (" %s\n", "-e, --errors-only");
935 printf (" %s\n", _("Display only devices/mountpoints with errors"));
936 printf (" %s\n", "-f, --freespace-ignore-reserved");
937 printf (" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
938 printf (" %s\n", "-g, --group=NAME");
939 printf (" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
940 printf (" %s\n", "-k, --kilobytes");
941 printf (" %s\n", _("Same as '--units kB'"));
942 printf (" %s\n", "-l, --local");
943 printf (" %s\n", _("Only check local filesystems"));
944 printf (" %s\n", "-L, --stat-remote-fs");
945 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
946 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
947 printf (" %s\n", "-M, --mountpoint");
948 printf (" %s\n", _("Display the mountpoint instead of the partition"));
949 printf (" %s\n", "-m, --megabytes");
950 printf (" %s\n", _("Same as '--units MB'"));
951 printf (" %s\n", "-A, --all");
952 printf (" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
953 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
954 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
955 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
956 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
957 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
958 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
959 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
960 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
961 printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
962 printf (" %s\n", "-u, --units=STRING");
963 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
964 printf (UT_VERBOSE);
965 printf (" %s\n", "-X, --exclude-type=TYPE");
966 printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
967 printf (" %s\n", "-N, --include-type=TYPE");
968 printf (" %s\n", _("Check only filesystems of indicated type (may be repeated)"));
970 printf ("\n");
971 printf ("%s\n", _("Examples:"));
972 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
973 printf (" %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
974 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
975 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
976 printf (" %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
977 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
978 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
980 printf (UT_SUPPORT);
985 void
986 print_usage (void)
988 printf ("%s\n", _("Usage:"));
989 printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
990 printf ("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
991 printf ("[-t timeout] [-u unit] [-v] [-X type] [-N type]\n");
994 void
995 stat_path (struct parameter_list *p)
997 /* Stat entry to check that dir exists and is accessible */
998 if (verbose >= 3)
999 printf("calling stat on %s\n", p->name);
1000 if (stat (p->name, &stat_buf[0])) {
1001 if (verbose >= 3)
1002 printf("stat failed on %s\n", p->name);
1003 printf("DISK %s - ", _("CRITICAL"));
1004 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
1009 void
1010 get_stats (struct parameter_list *p, struct fs_usage *fsp) {
1011 struct parameter_list *p_list;
1012 struct fs_usage tmpfsp;
1013 int first = 1;
1015 if (p->group == NULL) {
1016 get_path_stats(p,fsp);
1017 } else {
1018 /* find all group members */
1019 for (p_list = path_select_list; p_list; p_list=p_list->name_next) {
1020 #ifdef __CYGWIN__
1021 if (strncmp(p_list->name, "/cygdrive/", 10) != 0)
1022 continue;
1023 #endif
1024 if (p_list->group && ! (strcmp(p_list->group, p->group))) {
1025 stat_path(p_list);
1026 get_fs_usage (p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
1027 get_path_stats(p_list, &tmpfsp);
1028 if (verbose >= 3)
1029 printf("Group %s: adding %llu blocks sized %llu, (%s) used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
1030 p_list->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, p_list->dfree_units,
1031 p_list->dtotal_units, mult);
1033 /* prevent counting the first FS of a group twice since its parameter_list entry
1034 * is used to carry the information of all file systems of the entire group */
1035 if (! first) {
1036 p->total += p_list->total;
1037 p->available += p_list->available;
1038 p->available_to_root += p_list->available_to_root;
1039 p->used += p_list->used;
1041 p->dused_units += p_list->dused_units;
1042 p->dfree_units += p_list->dfree_units;
1043 p->dtotal_units += p_list->dtotal_units;
1044 p->inodes_total += p_list->inodes_total;
1045 p->inodes_free += p_list->inodes_free;
1046 p->inodes_free_to_root += p_list->inodes_free_to_root;
1047 p->inodes_used += p_list->inodes_used;
1049 first = 0;
1051 if (verbose >= 3)
1052 printf("Group %s now has: used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
1053 p->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p->best_match->me_mountdir, p->dused_units,
1054 p->dfree_units, p->dtotal_units, mult);
1056 /* modify devname and mountdir for output */
1057 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1059 /* finally calculate percentages for either plain FS or summed up group */
1060 p->dused_pct = calculate_percent( p->used, p->used + p->available ); /* used + available can never be > uintmax */
1061 p->dfree_pct = 100 - p->dused_pct;
1062 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1063 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1067 void
1068 get_path_stats (struct parameter_list *p, struct fs_usage *fsp) {
1069 /* 2007-12-08 - Workaround for Gnulib reporting insanely high available
1070 * space on BSD (the actual value should be negative but fsp->fsu_bavail
1071 * is unsigned) */
1072 p->available = fsp->fsu_bavail > fsp->fsu_bfree ? 0 : fsp->fsu_bavail;
1073 p->available_to_root = fsp->fsu_bfree;
1074 p->used = fsp->fsu_blocks - fsp->fsu_bfree;
1075 if (freespace_ignore_reserved) {
1076 /* option activated : we substract the root-reserved space from the total */
1077 p->total = fsp->fsu_blocks - p->available_to_root + p->available;
1078 } else {
1079 /* default behaviour : take all the blocks into account */
1080 p->total = fsp->fsu_blocks;
1083 p->dused_units = p->used*fsp->fsu_blocksize/mult;
1084 p->dfree_units = p->available*fsp->fsu_blocksize/mult;
1085 p->dtotal_units = p->total*fsp->fsu_blocksize/mult;
1086 /* Free file nodes. Not sure the workaround is required, but in case...*/
1087 p->inodes_free = fsp->fsu_favail > fsp->fsu_ffree ? 0 : fsp->fsu_favail;
1088 p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */
1089 p->inodes_used = fsp->fsu_files - fsp->fsu_ffree;
1090 if (freespace_ignore_reserved) {
1091 /* option activated : we substract the root-reserved inodes from the total */
1092 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1093 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1094 p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free;
1095 } else {
1096 /* default behaviour : take all the inodes into account */
1097 p->inodes_total = fsp->fsu_files;
1099 np_add_name(&seen, p->best_match->me_mountdir);