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
11 *
12 *
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.
17 *
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.
22 *
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/>.
25 *
26 *
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. */
95 static struct name_list *fs_exclude_list;
97 static struct name_list *dp_exclude_list;
99 static struct parameter_list *path_select_list = NULL;
101 /* Linked list of mounted filesystems. */
102 static struct mount_entry *mount_list;
104 /* For long options that have no equivalent short option, use a
105 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
106 enum
107 {
108 SYNC_OPTION = CHAR_MAX + 1,
109 NO_SYNC_OPTION,
110 BLOCK_SIZE_OPTION
111 };
113 #ifdef _AIX
114 #pragma alloca
115 #endif
117 int process_arguments (int, char **);
118 void print_path (const char *mypath);
119 void set_all_thresholds (struct parameter_list *path);
120 int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *);
121 void print_help (void);
122 void print_usage (void);
123 double calculate_percent(uintmax_t, uintmax_t);
124 void stat_path (struct parameter_list *p);
125 void get_stats (struct parameter_list *p, struct fs_usage *fsp);
126 void get_path_stats (struct parameter_list *p, struct fs_usage *fsp);
128 double w_dfp = -1.0;
129 double c_dfp = -1.0;
130 char *path;
131 char *exclude_device;
132 char *units;
133 uintmax_t mult = 1024 * 1024;
134 int verbose = 0;
135 int erronly = FALSE;
136 int display_mntp = FALSE;
137 int exact_match = FALSE;
138 char *warn_freespace_units = NULL;
139 char *crit_freespace_units = NULL;
140 char *warn_freespace_percent = NULL;
141 char *crit_freespace_percent = NULL;
142 char *warn_usedspace_units = NULL;
143 char *crit_usedspace_units = NULL;
144 char *warn_usedspace_percent = NULL;
145 char *crit_usedspace_percent = NULL;
146 char *warn_usedinodes_percent = NULL;
147 char *crit_usedinodes_percent = NULL;
148 char *warn_freeinodes_percent = NULL;
149 char *crit_freeinodes_percent = NULL;
150 int path_selected = FALSE;
151 char *group = NULL;
152 struct stat *stat_buf;
153 struct name_list *seen = NULL;
156 int
157 main (int argc, char **argv)
158 {
159 int result = STATE_UNKNOWN;
160 int disk_result = STATE_UNKNOWN;
161 char *output;
162 char *details;
163 char *perf;
164 char *preamble;
165 double inode_space_pct;
166 double warning_high_tide;
167 double critical_high_tide;
168 int temp_result;
170 struct mount_entry *me;
171 struct fs_usage fsp, tmpfsp;
172 struct parameter_list *temp_list, *path;
174 preamble = strdup (" - free space:");
175 output = strdup ("");
176 details = strdup ("");
177 perf = strdup ("");
178 stat_buf = malloc(sizeof *stat_buf);
180 setlocale (LC_ALL, "");
181 bindtextdomain (PACKAGE, LOCALEDIR);
182 textdomain (PACKAGE);
184 mount_list = read_file_system_list (0);
186 /* Parse extra opts if any */
187 argv = np_extra_opts (&argc, argv, progname);
189 if (process_arguments (argc, argv) == ERROR)
190 usage4 (_("Could not parse arguments"));
192 /* If a list of paths has not been selected, find entire
193 mount list and create list of paths
194 */
195 if (path_selected == FALSE) {
196 for (me = mount_list; me; me = me->me_next) {
197 if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
198 path = np_add_parameter(&path_select_list, me->me_mountdir);
199 }
200 path->best_match = me;
201 path->group = group;
202 set_all_thresholds(path);
203 }
204 }
205 np_set_best_match(path_select_list, mount_list, exact_match);
207 /* Error if no match found for specified paths */
208 temp_list = path_select_list;
210 while (temp_list) {
211 if (! temp_list->best_match) {
212 die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), temp_list->name);
213 }
215 temp_list = temp_list->name_next;
216 }
218 /* Process for every path in list */
219 for (path = path_select_list; path; path=path->name_next) {
221 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
222 printf("Thresholds(pct) for %s warn: %f crit %f\n",path->name, path->freespace_percent->warning->end,
223 path->freespace_percent->critical->end);
225 if (verbose >= 3 && path->group != NULL)
226 printf("Group of %s: %s\n",path->name,path->group);
228 /* reset disk result */
229 disk_result = STATE_UNKNOWN;
231 me = path->best_match;
233 /* Filters */
235 /* Remove filesystems already seen */
236 if (np_seen_name(seen, me->me_mountdir)) {
237 continue;
238 }
239 np_add_name(&seen, me->me_mountdir);
241 if (path->group == NULL) {
242 /* Skip remote filesystems if we're not interested in them */
243 if (me->me_remote && show_local_fs) {
244 if (stat_remote_fs)
245 stat_path(path);
246 continue;
247 /* Skip pseudo fs's if we haven't asked for all fs's */
248 } else if (me->me_dummy && !show_all_fs) {
249 continue;
250 /* Skip excluded fstypes */
251 } else if (fs_exclude_list && np_find_name (fs_exclude_list, me->me_type)) {
252 continue;
253 /* Skip excluded fs's */
254 } else if (dp_exclude_list &&
255 (np_find_name (dp_exclude_list, me->me_devname) ||
256 np_find_name (dp_exclude_list, me->me_mountdir))) {
257 continue;
258 }
260 stat_path(path);
261 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
262 }
264 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
265 get_stats (path, &fsp);
267 if (verbose >= 3) {
268 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",
269 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);
270 }
272 /* Threshold comparisons */
274 temp_result = get_status(path->dfree_units, path->freespace_units);
275 if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
276 disk_result = max_state( disk_result, temp_result );
278 temp_result = get_status(path->dfree_pct, path->freespace_percent);
279 if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
280 disk_result = max_state( disk_result, temp_result );
282 temp_result = get_status(path->dused_units, path->usedspace_units);
283 if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
284 disk_result = max_state( disk_result, temp_result );
286 temp_result = get_status(path->dused_pct, path->usedspace_percent);
287 if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
288 disk_result = max_state( disk_result, temp_result );
290 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
291 if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
292 disk_result = max_state( disk_result, temp_result );
294 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
295 if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
296 disk_result = max_state( disk_result, temp_result );
298 result = max_state(result, disk_result);
300 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
301 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
302 data. Assumption that start=0. Roll on new syntax...
303 */
305 /* *_high_tide must be reinitialized at each run */
306 warning_high_tide = UINT_MAX;
307 critical_high_tide = UINT_MAX;
309 if (path->freespace_units->warning != NULL) {
310 warning_high_tide = path->dtotal_units - path->freespace_units->warning->end;
311 }
312 if (path->freespace_percent->warning != NULL) {
313 warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freespace_percent->warning->end/100)*path->dtotal_units ));
314 }
315 if (path->freespace_units->critical != NULL) {
316 critical_high_tide = path->dtotal_units - path->freespace_units->critical->end;
317 }
318 if (path->freespace_percent->critical != NULL) {
319 critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freespace_percent->critical->end/100)*path->dtotal_units ));
320 }
322 /* Nb: *_high_tide are unset when == UINT_MAX */
323 asprintf (&perf, "%s %s", perf,
324 perfdata ((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
325 path->dused_units, units,
326 (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide,
327 (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide,
328 TRUE, 0,
329 TRUE, path->dtotal_units));
331 if (disk_result==STATE_OK && erronly && !verbose)
332 continue;
334 asprintf (&output, "%s %s %.0f %s (%.0f%%",
335 output,
336 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
337 path->dfree_units,
338 units,
339 path->dfree_pct);
340 if (path->dused_inodes_percent < 0) {
341 asprintf(&output, "%s inode=-);", output);
342 } else {
343 asprintf(&output, "%s inode=%.0f%%);", output, path->dfree_inodes_percent );
344 }
346 /* TODO: Need to do a similar debug line
347 asprintf (&details, _("%s\n\
348 %.0f of %.0f %s (%.0f%% inode=%.0f%%) free on %s (type %s mounted on %s) warn:%lu crit:%lu warn%%:%.0f%% crit%%:%.0f%%"),
349 details, dfree_units, dtotal_units, units, dfree_pct, inode_space_pct,
350 me->me_devname, me->me_type, me->me_mountdir,
351 (unsigned long)w_df, (unsigned long)c_df, w_dfp, c_dfp);
352 */
354 }
356 }
358 if (verbose >= 2)
359 asprintf (&output, "%s%s", output, details);
362 printf ("DISK %s%s%s|%s\n", state_text (result), (erronly && result==STATE_OK) ? "" : preamble, output, perf);
363 return result;
364 }
367 double calculate_percent(uintmax_t value, uintmax_t total) {
368 double pct = -1;
369 /* I don't understand the below, but it is taken from coreutils' df */
370 /* Seems to be calculating pct, in the best possible way */
371 if (value <= TYPE_MAXIMUM(uintmax_t) / 100
372 && total != 0) {
373 uintmax_t u100 = value * 100;
374 pct = u100 / total + (u100 % total != 0);
375 } else {
376 /* Possible rounding errors - see coreutils' df for more explanation */
377 double u = value;
378 double t = total;
379 if (t) {
380 long int lipct = pct = u * 100 / t;
381 double ipct = lipct;
383 /* Like 'pct = ceil (dpct);', but without ceil - from coreutils again */
384 if (ipct - 1 < pct && pct <= ipct + 1)
385 pct = ipct + (ipct < pct);
386 }
387 }
388 return pct;
389 }
391 /* process command-line arguments */
392 int
393 process_arguments (int argc, char **argv)
394 {
395 int c, err;
396 struct parameter_list *se;
397 struct parameter_list *temp_list = NULL, *previous = NULL;
398 struct parameter_list *temp_path_select_list = NULL;
399 struct mount_entry *me, *temp_me;
400 int result = OK;
401 regex_t re;
402 int cflags = REG_NOSUB | REG_EXTENDED;
403 int default_cflags = cflags;
404 char errbuf[MAX_INPUT_BUFFER];
405 int fnd = 0;
407 int option = 0;
408 static struct option longopts[] = {
409 {"timeout", required_argument, 0, 't'},
410 {"warning", required_argument, 0, 'w'},
411 {"critical", required_argument, 0, 'c'},
412 {"iwarning", required_argument, 0, 'W'},
413 /* Dang, -C is taken. We might want to reshuffle this. */
414 {"icritical", required_argument, 0, 'K'},
415 {"kilobytes", no_argument, 0, 'k'},
416 {"megabytes", no_argument, 0, 'm'},
417 {"units", required_argument, 0, 'u'},
418 {"path", required_argument, 0, 'p'},
419 {"partition", required_argument, 0, 'p'},
420 {"exclude_device", required_argument, 0, 'x'},
421 {"exclude-type", required_argument, 0, 'X'},
422 {"group", required_argument, 0, 'g'},
423 {"eregi-path", required_argument, 0, 'R'},
424 {"eregi-partition", required_argument, 0, 'R'},
425 {"ereg-path", required_argument, 0, 'r'},
426 {"ereg-partition", required_argument, 0, 'r'},
427 {"ignore-ereg-path", required_argument, 0, 'i'},
428 {"ignore-ereg-partition", required_argument, 0, 'i'},
429 {"ignore-eregi-path", required_argument, 0, 'I'},
430 {"ignore-eregi-partition", required_argument, 0, 'I'},
431 {"local", no_argument, 0, 'l'},
432 {"stat-remote-fs", no_argument, 0, 'L'},
433 {"mountpoint", no_argument, 0, 'M'},
434 {"errors-only", no_argument, 0, 'e'},
435 {"exact-match", no_argument, 0, 'E'},
436 {"all", no_argument, 0, 'A'},
437 {"verbose", no_argument, 0, 'v'},
438 {"quiet", no_argument, 0, 'q'},
439 {"clear", no_argument, 0, 'C'},
440 {"version", no_argument, 0, 'V'},
441 {"help", no_argument, 0, 'h'},
442 {0, 0, 0, 0}
443 };
445 if (argc < 2)
446 return ERROR;
448 np_add_name(&fs_exclude_list, "iso9660");
450 for (c = 1; c < argc; c++)
451 if (strcmp ("-to", argv[c]) == 0)
452 strcpy (argv[c], "-t");
454 while (1) {
455 c = getopt_long (argc, argv, "+?VqhveCt:c:w:K:W:u:p:x:X:mklLg:R:r:i:I:MEA", longopts, &option);
457 if (c == -1 || c == EOF)
458 break;
460 switch (c) {
461 case 't': /* timeout period */
462 if (is_integer (optarg)) {
463 timeout_interval = atoi (optarg);
464 break;
465 }
466 else {
467 usage2 (_("Timeout interval must be a positive integer"), optarg);
468 }
470 /* See comments for 'c' */
471 case 'w': /* warning threshold */
472 if (strstr(optarg, "%")) {
473 if (*optarg == '@') {
474 warn_freespace_percent = optarg;
475 } else {
476 asprintf(&warn_freespace_percent, "@%s", optarg);
477 }
478 } else {
479 if (*optarg == '@') {
480 warn_freespace_units = optarg;
481 } else {
482 asprintf(&warn_freespace_units, "@%s", optarg);
483 }
484 }
485 break;
487 /* Awful mistake where the range values do not make sense. Normally,
488 you alert if the value is within the range, but since we are using
489 freespace, we have to alert if outside the range. Thus we artifically
490 force @ at the beginning of the range, so that it is backwards compatible
491 */
492 case 'c': /* critical threshold */
493 if (strstr(optarg, "%")) {
494 if (*optarg == '@') {
495 crit_freespace_percent = optarg;
496 } else {
497 asprintf(&crit_freespace_percent, "@%s", optarg);
498 }
499 } else {
500 if (*optarg == '@') {
501 crit_freespace_units = optarg;
502 } else {
503 asprintf(&crit_freespace_units, "@%s", optarg);
504 }
505 }
506 break;
508 case 'W': /* warning inode threshold */
509 if (*optarg == '@') {
510 warn_freeinodes_percent = optarg;
511 } else {
512 asprintf(&warn_freeinodes_percent, "@%s", optarg);
513 }
514 break;
515 case 'K': /* critical inode threshold */
516 if (*optarg == '@') {
517 crit_freeinodes_percent = optarg;
518 } else {
519 asprintf(&crit_freeinodes_percent, "@%s", optarg);
520 }
521 break;
522 case 'u':
523 if (units)
524 free(units);
525 if (! strcmp (optarg, "bytes")) {
526 mult = (uintmax_t)1;
527 units = strdup ("B");
528 } else if (! strcmp (optarg, "kB")) {
529 mult = (uintmax_t)1024;
530 units = strdup ("kB");
531 } else if (! strcmp (optarg, "MB")) {
532 mult = (uintmax_t)1024 * 1024;
533 units = strdup ("MB");
534 } else if (! strcmp (optarg, "GB")) {
535 mult = (uintmax_t)1024 * 1024 * 1024;
536 units = strdup ("GB");
537 } else if (! strcmp (optarg, "TB")) {
538 mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
539 units = strdup ("TB");
540 } else {
541 die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
542 }
543 if (units == NULL)
544 die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
545 break;
546 case 'k': /* display mountpoint */
547 mult = 1024;
548 if (units)
549 free(units);
550 units = strdup ("kB");
551 break;
552 case 'm': /* display mountpoint */
553 mult = 1024 * 1024;
554 if (units)
555 free(units);
556 units = strdup ("MB");
557 break;
558 case 'L':
559 stat_remote_fs = 1;
560 case 'l':
561 show_local_fs = 1;
562 break;
563 case 'p': /* select path */
564 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
565 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
566 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
567 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
568 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
569 }
571 /* add parameter if not found. overwrite thresholds if path has already been added */
572 if (! (se = np_find_parameter(path_select_list, optarg))) {
573 se = np_add_parameter(&path_select_list, optarg);
574 }
575 se->group = group;
576 set_all_thresholds(se);
578 /* With autofs, it is required to stat() the path before re-populating the mount_list */
579 stat_path(se);
580 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
581 * pointers are copied around. One of the reason it wasn't done yet is that other parts
582 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
583 mount_list = read_file_system_list (0);
584 np_set_best_match(se, mount_list, exact_match);
586 path_selected = TRUE;
587 break;
588 case 'x': /* exclude path or partition */
589 np_add_name(&dp_exclude_list, optarg);
590 break;
591 case 'X': /* exclude file system type */
592 np_add_name(&fs_exclude_list, optarg);
593 break;
594 case 'v': /* verbose */
595 verbose++;
596 break;
597 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
598 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
599 erronly = TRUE;
600 break;
601 case 'e':
602 erronly = TRUE;
603 break;
604 case 'E':
605 if (path_selected)
606 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
607 exact_match = TRUE;
608 break;
609 case 'g':
610 if (path_selected)
611 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
612 group = optarg;
613 break;
614 case 'I':
615 cflags |= REG_ICASE;
616 case 'i':
617 if (!path_selected)
618 die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
619 err = regcomp(&re, optarg, cflags);
620 if (err != 0) {
621 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
622 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
623 }
625 temp_list = path_select_list;
627 previous = NULL;
628 while (temp_list) {
629 if (temp_list->best_match) {
630 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
632 if (verbose >=3)
633 printf("ignoring %s matching regex\n", temp_list->name);
635 temp_list = np_del_parameter(temp_list, previous);
636 /* pointer to first element needs to be updated if first item gets deleted */
637 if (previous == NULL)
638 path_select_list = temp_list;
639 } else {
640 previous = temp_list;
641 temp_list = temp_list->name_next;
642 }
643 } else {
644 previous = temp_list;
645 temp_list = temp_list->name_next;
646 }
647 }
650 cflags = default_cflags;
651 break;
653 case 'A':
654 optarg = strdup(".*");
655 case 'R':
656 cflags |= REG_ICASE;
657 case 'r':
658 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
659 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
660 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
661 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
662 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n"));
663 }
665 err = regcomp(&re, optarg, cflags);
666 if (err != 0) {
667 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
668 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
669 }
671 for (me = mount_list; me; me = me->me_next) {
672 if (np_regex_match_mount_entry(me, &re)) {
673 fnd = TRUE;
674 if (verbose >= 3)
675 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
677 /* add parameter if not found. overwrite thresholds if path has already been added */
678 if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
679 se = np_add_parameter(&path_select_list, me->me_mountdir);
680 }
681 se->group = group;
682 set_all_thresholds(se);
683 }
684 }
686 if (!fnd)
687 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
688 _("Regular expression did not match any path or disk"), optarg);
690 fnd = FALSE;
691 path_selected = TRUE;
692 np_set_best_match(path_select_list, mount_list, exact_match);
693 cflags = default_cflags;
695 break;
696 case 'M': /* display mountpoint */
697 display_mntp = TRUE;
698 break;
699 case 'C':
700 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
701 if (path_selected == FALSE) {
702 struct parameter_list *path;
703 for (me = mount_list; me; me = me->me_next) {
704 if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
705 path = np_add_parameter(&path_select_list, me->me_mountdir);
706 path->best_match = me;
707 path->group = group;
708 set_all_thresholds(path);
709 }
710 }
711 warn_freespace_units = NULL;
712 crit_freespace_units = NULL;
713 warn_usedspace_units = NULL;
714 crit_usedspace_units = NULL;
715 warn_freespace_percent = NULL;
716 crit_freespace_percent = NULL;
717 warn_usedspace_percent = NULL;
718 crit_usedspace_percent = NULL;
719 warn_usedinodes_percent = NULL;
720 crit_usedinodes_percent = NULL;
721 warn_freeinodes_percent = NULL;
722 crit_freeinodes_percent = NULL;
724 path_selected = FALSE;
725 group = NULL;
726 break;
727 case 'V': /* version */
728 print_revision (progname, NP_VERSION);
729 exit (STATE_OK);
730 case 'h': /* help */
731 print_help ();
732 exit (STATE_OK);
733 case '?': /* help */
734 usage (_("Unknown argument"));
735 }
736 }
738 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
739 c = optind;
740 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
741 warn_usedspace_percent = argv[c++];
743 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
744 crit_usedspace_percent = argv[c++];
746 if (argc > c && path == NULL) {
747 se = np_add_parameter(&path_select_list, strdup(argv[c++]));
748 path_selected = TRUE;
749 set_all_thresholds(se);
750 }
752 if (units == NULL) {
753 units = strdup ("MB");
754 mult = (uintmax_t)1024 * 1024;
755 }
757 return TRUE;
758 }
762 void
763 print_path (const char *mypath)
764 {
765 if (mypath == NULL)
766 printf ("\n");
767 else
768 printf (_(" for %s\n"), mypath);
769 }
772 void
773 set_all_thresholds (struct parameter_list *path)
774 {
775 if (path->freespace_units != NULL) free(path->freespace_units);
776 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
777 if (path->freespace_percent != NULL) free (path->freespace_percent);
778 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
779 if (path->usedspace_units != NULL) free (path->usedspace_units);
780 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
781 if (path->usedspace_percent != NULL) free (path->usedspace_percent);
782 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
783 if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
784 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
785 if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
786 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
787 }
789 /* TODO: Remove?
791 int
792 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
793 {
794 if (w < 0 && c < 0 && wp < 0.0 && cp < 0.0) {
795 printf (_("INPUT ERROR: No thresholds specified"));
796 print_path (mypath);
797 return ERROR;
798 }
799 else if ((wp >= 0.0 || cp >= 0.0) &&
800 (wp < 0.0 || cp < 0.0 || wp > 100.0 || cp > 100.0 || cp > wp)) {
801 printf (_("\
802 INPUT ERROR: C_DFP (%f) should be less than W_DFP (%.1f) and both should be between zero and 100 percent, inclusive"),
803 cp, wp);
804 print_path (mypath);
805 return ERROR;
806 }
807 else if ((iwp >= 0.0 || icp >= 0.0) &&
808 (iwp < 0.0 || icp < 0.0 || iwp > 100.0 || icp > 100.0 || icp > iwp)) {
809 printf (_("\
810 INPUT ERROR: C_IDFP (%f) should be less than W_IDFP (%.1f) and both should be between zero and 100 percent, inclusive"),
811 icp, iwp);
812 print_path (mypath);
813 return ERROR;
814 }
815 else if ((w > 0 || c > 0) && (w == 0 || c == 0 || c > w)) {
816 printf (_("\
817 INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greater than zero"),
818 (unsigned long)c, (unsigned long)w);
819 print_path (mypath);
820 return ERROR;
821 }
823 return OK;
824 }
826 */
834 void
835 print_help (void)
836 {
837 print_revision (progname, NP_VERSION);
839 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
840 printf (COPYRIGHT, copyright, email);
842 printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
843 printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
845 printf ("\n\n");
847 print_usage ();
849 printf (UT_HELP_VRSN);
850 printf (UT_EXTRA_OPTS);
852 printf (" %s\n", "-w, --warning=INTEGER");
853 printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
854 printf (" %s\n", "-w, --warning=PERCENT%");
855 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
856 printf (" %s\n", "-c, --critical=INTEGER");
857 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
858 printf (" %s\n", "-c, --critical=PERCENT%");
859 printf (" %s\n", _("Exit with CRITCAL status if less than PERCENT of disk space is free"));
860 printf (" %s\n", "-W, --iwarning=PERCENT%");
861 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
862 printf (" %s\n", "-K, --icritical=PERCENT%");
863 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
864 printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
865 printf (" %s\n", _("Path or partition (may be repeated)"));
866 printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
867 printf (" %s\n", _("Ignore device (only works if -p unspecified)"));
868 printf (" %s\n", "-C, --clear");
869 printf (" %s\n", _("Clear thresholds"));
870 printf (" %s\n", "-E, --exact-match");
871 printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
872 printf (" %s\n", "-e, --errors-only");
873 printf (" %s\n", _("Display only devices/mountpoints with errors"));
874 printf (" %s\n", "-g, --group=NAME");
875 printf (" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
876 printf (" %s\n", "-k, --kilobytes");
877 printf (" %s\n", _("Same as '--units kB'"));
878 printf (" %s\n", "-l, --local");
879 printf (" %s\n", _("Only check local filesystems"));
880 printf (" %s\n", "-L, --stat-remote-fs");
881 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
882 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
883 printf (" %s\n", "-M, --mountpoint");
884 printf (" %s\n", _("Display the mountpoint instead of the partition"));
885 printf (" %s\n", "-m, --megabytes");
886 printf (" %s\n", _("Same as '--units MB'"));
887 printf (" %s\n", "-A, --all");
888 printf (" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
889 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
890 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
891 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
892 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
893 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
894 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
895 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
896 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
897 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
898 printf (" %s\n", "-u, --units=STRING");
899 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
900 printf (UT_VERBOSE);
901 printf (" %s\n", "-X, --exclude-type=TYPE");
902 printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
904 printf ("\n");
905 printf ("%s\n", _("Examples:"));
906 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
907 printf (" %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
908 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
909 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
910 printf (" %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
911 printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
912 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
914 printf (UT_SUPPORT);
915 }
919 void
920 print_usage (void)
921 {
922 printf ("%s\n", _("Usage:"));
923 printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
924 printf ("[-C] [-E] [-e] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
925 printf ("[-t timeout] [-u unit] [-v] [-X type]\n");
926 }
928 void
929 stat_path (struct parameter_list *p)
930 {
931 /* Stat entry to check that dir exists and is accessible */
932 if (verbose >= 3)
933 printf("calling stat on %s\n", p->name);
934 if (stat (p->name, &stat_buf[0])) {
935 if (verbose >= 3)
936 printf("stat failed on %s\n", p->name);
937 printf("DISK %s - ", _("CRITICAL"));
938 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
939 }
940 }
943 void
944 get_stats (struct parameter_list *p, struct fs_usage *fsp) {
945 struct parameter_list *p_list;
946 struct fs_usage tmpfsp;
947 int first = 1;
949 if (p->group == NULL) {
950 get_path_stats(p,fsp);
951 } else {
952 /* find all group members */
953 for (p_list = path_select_list; p_list; p_list=p_list->name_next) {
954 if (p_list->group && ! (strcmp(p_list->group, p->group))) {
955 stat_path(p_list);
956 get_fs_usage (p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
957 get_path_stats(p_list, &tmpfsp);
958 if (verbose >= 3)
959 printf("Group %s: adding %llu blocks sized %llu, (%s) used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
960 p_list->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, p_list->dfree_units,
961 p_list->dtotal_units, mult);
963 /* prevent counting the first FS of a group twice since its parameter_list entry
964 * is used to carry the information of all file systems of the entire group */
965 if (! first) {
966 p->total += p_list->total;
967 p->available += p_list->available;
968 p->available_to_root += p_list->available_to_root;
969 p->used += p_list->used;
971 p->dused_units += p_list->dused_units;
972 p->dfree_units += p_list->dfree_units;
973 p->dtotal_units += p_list->dtotal_units;
974 p->inodes_total += p_list->inodes_total;
975 p->inodes_free += p_list->inodes_free;
976 }
977 first = 0;
978 }
979 if (verbose >= 3)
980 printf("Group %s now has: used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
981 p->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p->best_match->me_mountdir, p->dused_units,
982 p->dfree_units, p->dtotal_units, mult);
983 }
984 /* modify devname and mountdir for output */
985 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
986 }
987 /* finally calculate percentages for either plain FS or summed up group */
988 p->dused_pct = calculate_percent( p->used, p->used + p->available ); /* used + available can never be > uintmax */
989 p->dfree_pct = 100 - p->dused_pct;
990 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
991 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
993 }
995 void
996 get_path_stats (struct parameter_list *p, struct fs_usage *fsp) {
997 p->total = fsp->fsu_blocks;
998 /* 2007-12-08 - Workaround for Gnulib reporting insanely high available
999 * space on BSD (the actual value should be negative but fsp->fsu_bavail
1000 * is unsigned) */
1001 p->available = fsp->fsu_bavail > fsp->fsu_bfree ? 0 : fsp->fsu_bavail;
1002 p->available_to_root = fsp->fsu_bfree;
1003 p->used = p->total - p->available_to_root;
1005 p->dused_units = p->used*fsp->fsu_blocksize/mult;
1006 p->dfree_units = p->available*fsp->fsu_blocksize/mult;
1007 p->dtotal_units = p->total*fsp->fsu_blocksize/mult;
1008 p->inodes_total = fsp->fsu_files; /* Total file nodes. */
1009 p->inodes_free = fsp->fsu_ffree; /* Free file nodes. */
1010 np_add_name(&seen, p->best_match->me_mountdir);
1011 }