1 /*****************************************************************************
2 *
3 * Nagios check_disk plugin
4 *
5 * License: GPL
6 * Copyright (c) 1999-2008 Nagios Plugins Development Team
7 *
8 * Last Modified: $Date$
9 *
10 * Description:
11 *
12 * This file contains the check_disk plugin
13 *
14 *
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 *
28 * $Id$
29 *
30 *****************************************************************************/
32 const char *progname = "check_disk";
33 const char *program_name = "check_disk"; /* Required for coreutils libs */
34 const char *revision = "$Revision$";
35 const char *copyright = "1999-2008";
36 const char *email = "nagiosplug-devel@lists.sourceforge.net";
39 #include "common.h"
40 #ifdef HAVE_SYS_STAT_H
41 # include <sys/stat.h>
42 #endif
43 #if HAVE_INTTYPES_H
44 # include <inttypes.h>
45 #endif
46 #include <assert.h>
47 #include "popen.h"
48 #include "utils.h"
49 #include "utils_disk.h"
50 #include <stdarg.h>
51 #include "fsusage.h"
52 #include "mountlist.h"
53 #include "intprops.h" /* necessary for TYPE_MAXIMUM */
54 #if HAVE_LIMITS_H
55 # include <limits.h>
56 #endif
57 #include "regex.h"
60 /* If nonzero, show inode information. */
61 static int inode_format = 1;
63 /* If nonzero, show even filesystems with zero size or
64 uninteresting types. */
65 static int show_all_fs = 1;
67 /* If nonzero, show only local filesystems. */
68 static int show_local_fs = 0;
70 /* If nonzero, show only local filesystems but call stat() on remote ones. */
71 static int stat_remote_fs = 0;
73 /* If positive, the units to use when printing sizes;
74 if negative, the human-readable base. */
75 /* static int output_block_size; */
77 /* If nonzero, invoke the `sync' system call before getting any usage data.
78 Using this option can make df very slow, especially with many or very
79 busy disks. Note that this may make a difference on some systems --
80 SunOs4.1.3, for one. It is *not* necessary on Linux. */
81 /* static int require_sync = 0; */
83 /* Linked list of filesystem types to display.
84 If `fs_select_list' is NULL, list all types.
85 This table is generated dynamically from command-line options,
86 rather than hardcoding into the program what it thinks are the
87 valid filesystem types; let the user specify any filesystem type
88 they want to, and if there are any filesystems of that type, they
89 will be shown.
91 Some filesystem types:
92 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
94 /* static struct parameter_list *fs_select_list; */
96 /* Linked list of filesystem types to omit.
97 If the list is empty, don't exclude any types. */
99 static struct name_list *fs_exclude_list;
101 static struct name_list *dp_exclude_list;
103 static struct parameter_list *path_select_list = NULL;
105 /* Linked list of mounted filesystems. */
106 static struct mount_entry *mount_list;
108 /* For long options that have no equivalent short option, use a
109 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
110 enum
111 {
112 SYNC_OPTION = CHAR_MAX + 1,
113 NO_SYNC_OPTION,
114 BLOCK_SIZE_OPTION
115 };
117 #ifdef _AIX
118 #pragma alloca
119 #endif
121 int process_arguments (int, char **);
122 void print_path (const char *mypath);
123 void set_all_thresholds (struct parameter_list *path);
124 int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *);
125 void print_help (void);
126 void print_usage (void);
127 double calculate_percent(uintmax_t, uintmax_t);
128 void stat_path (struct parameter_list *p);
130 double w_dfp = -1.0;
131 double c_dfp = -1.0;
132 char *path;
133 char *exclude_device;
134 char *units;
135 uintmax_t mult = 1024 * 1024;
136 int verbose = 0;
137 int erronly = FALSE;
138 int display_mntp = FALSE;
139 int exact_match = FALSE;
140 char *warn_freespace_units = NULL;
141 char *crit_freespace_units = NULL;
142 char *warn_freespace_percent = NULL;
143 char *crit_freespace_percent = NULL;
144 char *warn_usedspace_units = NULL;
145 char *crit_usedspace_units = NULL;
146 char *warn_usedspace_percent = NULL;
147 char *crit_usedspace_percent = NULL;
148 char *warn_usedinodes_percent = NULL;
149 char *crit_usedinodes_percent = NULL;
150 char *warn_freeinodes_percent = NULL;
151 char *crit_freeinodes_percent = NULL;
152 int path_selected = FALSE;
153 char *group = NULL;
154 struct stat *stat_buf;
157 int
158 main (int argc, char **argv)
159 {
160 int result = STATE_UNKNOWN;
161 int disk_result = STATE_UNKNOWN;
162 char *output;
163 char *details;
164 char *perf;
165 char *preamble;
166 double inode_space_pct;
167 uintmax_t total, available, available_to_root, used;
168 double dfree_pct = -1, dused_pct = -1;
169 double dused_units, dfree_units, dtotal_units;
170 double dused_inodes_percent, dfree_inodes_percent;
171 double warning_high_tide;
172 double critical_high_tide;
173 int temp_result;
175 struct mount_entry *me;
176 struct fs_usage fsp, tmpfsp;
177 struct parameter_list *temp_list, *path;
178 struct name_list *seen = NULL;
180 preamble = strdup (" - free space:");
181 output = strdup ("");
182 details = strdup ("");
183 perf = strdup ("");
184 stat_buf = malloc(sizeof *stat_buf);
186 setlocale (LC_ALL, "");
187 bindtextdomain (PACKAGE, LOCALEDIR);
188 textdomain (PACKAGE);
190 mount_list = read_file_system_list (0);
192 /* Parse extra opts if any */
193 argv = np_extra_opts (&argc, argv, progname);
195 if (process_arguments (argc, argv) == ERROR)
196 usage4 (_("Could not parse arguments"));
198 /* If a list of paths has not been selected, find entire
199 mount list and create list of paths
200 */
201 if (path_selected == FALSE) {
202 for (me = mount_list; me; me = me->me_next) {
203 if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
204 path = np_add_parameter(&path_select_list, me->me_mountdir);
205 }
206 path->best_match = me;
207 path->group = group;
208 set_all_thresholds(path);
209 }
210 }
211 np_set_best_match(path_select_list, mount_list, exact_match);
213 /* Error if no match found for specified paths */
214 temp_list = path_select_list;
216 while (temp_list) {
217 if (! temp_list->best_match) {
218 die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), temp_list->name);
219 }
221 temp_list = temp_list->name_next;
222 }
224 /* Process for every path in list */
225 for (path = path_select_list; path; path=path->name_next) {
227 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
228 printf("Thresholds(pct) for %s warn: %f crit %f\n",path->name, path->freespace_percent->warning->end,
229 path->freespace_percent->critical->end);
231 if (verbose >= 3 && path->group != NULL)
232 printf("Group of %s: %s\n",path->name,path->group);
234 /* reset disk result */
235 disk_result = STATE_UNKNOWN;
237 me = path->best_match;
239 /* Filters */
241 /* Remove filesystems already seen */
242 if (np_seen_name(seen, me->me_mountdir)) {
243 continue;
244 } else {
245 if (path->group != NULL) {
246 /* find all group members */
247 fsp.fsu_blocksize = 0;
248 fsp.fsu_blocks = 0;
249 fsp.fsu_bfree = 0;
250 fsp.fsu_bavail = 0;
251 fsp.fsu_files = 0;
252 fsp.fsu_ffree = 0;
255 for (temp_list = path_select_list; temp_list; temp_list=temp_list->name_next) {
256 if (temp_list->group && ! (strcmp(temp_list->group, path->group))) {
258 stat_path(path);
259 get_fs_usage (temp_list->best_match->me_mountdir, temp_list->best_match->me_devname, &tmpfsp);
261 /* possibly differing blocksizes if disks are grouped. Calculating average */
262 fsp.fsu_blocksize = (fsp.fsu_blocksize * fsp.fsu_blocks + tmpfsp.fsu_blocksize * tmpfsp.fsu_blocks) / \
263 (fsp.fsu_blocks + tmpfsp.fsu_blocks); /* Size of a block. */
264 fsp.fsu_blocks += tmpfsp.fsu_blocks; /* Total blocks. */
265 fsp.fsu_bfree += tmpfsp.fsu_bfree; /* Free blocks available to superuser. */
266 /* Gnulib workaround - see comment about it a few lines below */
267 fsp.fsu_bavail += (tmpfsp.fsu_bavail > tmpfsp.fsu_bfree ? 0 : tmpfsp.fsu_bavail); /* Free blocks available to non-superuser. */
268 fsp.fsu_files += tmpfsp.fsu_files; /* Total file nodes. */
269 fsp.fsu_ffree += tmpfsp.fsu_ffree; /* Free file nodes. */
271 if (verbose >= 3)
272 printf("Group %s: add %llu blocks (%s) \n", path->group, tmpfsp.fsu_bavail, temp_list->name);
273 /* printf("Group %s: add %u blocks (%s)\n", temp_list->name); *//* path->group, tmpfsp.fsu_bavail, temp_list->name); */
275 np_add_name(&seen, temp_list->best_match->me_mountdir);
276 }
277 }
278 /* modify devname and mountdir for output */
279 me->me_mountdir = me->me_devname = path->group;
280 } else
281 np_add_name(&seen, me->me_mountdir);
282 }
284 if (path->group == NULL) {
285 /* Skip remote filesystems if we're not interested in them */
286 if (me->me_remote && show_local_fs) {
287 if (stat_remote_fs)
288 stat_path(path);
289 continue;
290 /* Skip pseudo fs's if we haven't asked for all fs's */
291 } else if (me->me_dummy && !show_all_fs) {
292 continue;
293 /* Skip excluded fstypes */
294 } else if (fs_exclude_list && np_find_name (fs_exclude_list, me->me_type)) {
295 continue;
296 /* Skip excluded fs's */
297 } else if (dp_exclude_list &&
298 (np_find_name (dp_exclude_list, me->me_devname) ||
299 np_find_name (dp_exclude_list, me->me_mountdir))) {
300 continue;
301 }
303 stat_path(path);
304 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
305 }
307 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
308 total = fsp.fsu_blocks;
309 /* 2007-12-08 - Workaround for Gnulib reporting insanely high available
310 * space on BSD (the actual value should be negative but fsp.fsu_bavail
311 * is unsigned) */
312 available = fsp.fsu_bavail > fsp.fsu_bfree ? 0 : fsp.fsu_bavail;
313 available_to_root = fsp.fsu_bfree;
314 used = total - available_to_root;
316 if (verbose >= 3)
317 printf ("For %s, total=%llu, available=%llu, available_to_root=%llu, used=%llu, fsp.fsu_files=%llu, fsp.fsu_ffree=%llu\n",
318 me->me_mountdir, total, available, available_to_root, used, fsp.fsu_files, fsp.fsu_ffree);
320 dused_pct = calculate_percent( used, used + available ); /* used + available can never be > uintmax */
322 dfree_pct = 100 - dused_pct;
323 dused_units = used*fsp.fsu_blocksize/mult;
324 dfree_units = available*fsp.fsu_blocksize/mult;
325 dtotal_units = total*fsp.fsu_blocksize/mult;
326 dused_inodes_percent = calculate_percent(fsp.fsu_files - fsp.fsu_ffree, fsp.fsu_files);
327 dfree_inodes_percent = 100 - dused_inodes_percent;
329 if (verbose >= 3) {
330 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",
331 me->me_mountdir, dused_pct, dfree_pct, dused_units, dfree_units, dtotal_units, dused_inodes_percent, dfree_inodes_percent, fsp.fsu_blocksize, mult);
332 }
334 /* Threshold comparisons */
336 temp_result = get_status(dfree_units, path->freespace_units);
337 if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
338 disk_result = max_state( disk_result, temp_result );
340 temp_result = get_status(dfree_pct, path->freespace_percent);
341 if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
342 disk_result = max_state( disk_result, temp_result );
344 temp_result = get_status(dused_units, path->usedspace_units);
345 if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
346 disk_result = max_state( disk_result, temp_result );
348 temp_result = get_status(dused_pct, path->usedspace_percent);
349 if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
350 disk_result = max_state( disk_result, temp_result );
352 temp_result = get_status(dused_inodes_percent, path->usedinodes_percent);
353 if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
354 disk_result = max_state( disk_result, temp_result );
356 temp_result = get_status(dfree_inodes_percent, path->freeinodes_percent);
357 if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
358 disk_result = max_state( disk_result, temp_result );
360 result = max_state(result, disk_result);
362 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
363 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
364 data. Assumption that start=0. Roll on new syntax...
365 */
367 /* *_high_tide must be reinitialized at each run */
368 warning_high_tide = UINT_MAX;
369 critical_high_tide = UINT_MAX;
371 if (path->freespace_units->warning != NULL) {
372 warning_high_tide = dtotal_units - path->freespace_units->warning->end;
373 }
374 if (path->freespace_percent->warning != NULL) {
375 warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freespace_percent->warning->end/100)*dtotal_units ));
376 }
377 if (path->freespace_units->critical != NULL) {
378 critical_high_tide = dtotal_units - path->freespace_units->critical->end;
379 }
380 if (path->freespace_percent->critical != NULL) {
381 critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freespace_percent->critical->end/100)*dtotal_units ));
382 }
384 /* Nb: *_high_tide are unset when == UINT_MAX */
385 asprintf (&perf, "%s %s", perf,
386 perfdata ((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
387 dused_units, units,
388 (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide,
389 (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide,
390 TRUE, 0,
391 TRUE, dtotal_units));
393 if (disk_result==STATE_OK && erronly && !verbose)
394 continue;
396 asprintf (&output, "%s %s %.0f %s (%.0f%%",
397 output,
398 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
399 dfree_units,
400 units,
401 dfree_pct);
402 if (dused_inodes_percent < 0) {
403 asprintf(&output, "%s inode=-);", output);
404 } else {
405 asprintf(&output, "%s inode=%.0f%%);", output, dfree_inodes_percent );
406 }
408 /* TODO: Need to do a similar debug line
409 asprintf (&details, _("%s\n\
410 %.0f of %.0f %s (%.0f%% inode=%.0f%%) free on %s (type %s mounted on %s) warn:%lu crit:%lu warn%%:%.0f%% crit%%:%.0f%%"),
411 details, dfree_units, dtotal_units, units, dfree_pct, inode_space_pct,
412 me->me_devname, me->me_type, me->me_mountdir,
413 (unsigned long)w_df, (unsigned long)c_df, w_dfp, c_dfp);
414 */
416 }
418 }
420 if (verbose >= 2)
421 asprintf (&output, "%s%s", output, details);
424 printf ("DISK %s%s%s|%s\n", state_text (result), (erronly && result==STATE_OK) ? "" : preamble, output, perf);
425 return result;
426 }
429 double calculate_percent(uintmax_t value, uintmax_t total) {
430 double pct = -1;
431 /* I don't understand the below, but it is taken from coreutils' df */
432 /* Seems to be calculating pct, in the best possible way */
433 if (value <= TYPE_MAXIMUM(uintmax_t) / 100
434 && total != 0) {
435 uintmax_t u100 = value * 100;
436 pct = u100 / total + (u100 % total != 0);
437 } else {
438 /* Possible rounding errors - see coreutils' df for more explanation */
439 double u = value;
440 double t = total;
441 if (t) {
442 long int lipct = pct = u * 100 / t;
443 double ipct = lipct;
445 /* Like 'pct = ceil (dpct);', but without ceil - from coreutils again */
446 if (ipct - 1 < pct && pct <= ipct + 1)
447 pct = ipct + (ipct < pct);
448 }
449 }
450 return pct;
451 }
453 /* process command-line arguments */
454 int
455 process_arguments (int argc, char **argv)
456 {
457 int c, err;
458 struct parameter_list *se;
459 struct parameter_list *temp_list = NULL, *previous = NULL;
460 struct parameter_list *temp_path_select_list = NULL;
461 struct mount_entry *me, *temp_me;
462 int result = OK;
463 regex_t re;
464 int cflags = REG_NOSUB | REG_EXTENDED;
465 int default_cflags = cflags;
466 char errbuf[MAX_INPUT_BUFFER];
467 int fnd = 0;
469 int option = 0;
470 static struct option longopts[] = {
471 {"timeout", required_argument, 0, 't'},
472 {"warning", required_argument, 0, 'w'},
473 {"critical", required_argument, 0, 'c'},
474 {"iwarning", required_argument, 0, 'W'},
475 /* Dang, -C is taken. We might want to reshuffle this. */
476 {"icritical", required_argument, 0, 'K'},
477 {"kilobytes", no_argument, 0, 'k'},
478 {"megabytes", no_argument, 0, 'm'},
479 {"units", required_argument, 0, 'u'},
480 {"path", required_argument, 0, 'p'},
481 {"partition", required_argument, 0, 'p'},
482 {"exclude_device", required_argument, 0, 'x'},
483 {"exclude-type", required_argument, 0, 'X'},
484 {"group", required_argument, 0, 'g'},
485 {"eregi-path", required_argument, 0, 'R'},
486 {"eregi-partition", required_argument, 0, 'R'},
487 {"ereg-path", required_argument, 0, 'r'},
488 {"ereg-partition", required_argument, 0, 'r'},
489 {"ignore-ereg-path", required_argument, 0, 'i'},
490 {"ignore-ereg-partition", required_argument, 0, 'i'},
491 {"ignore-eregi-path", required_argument, 0, 'I'},
492 {"ignore-eregi-partition", required_argument, 0, 'I'},
493 {"local", no_argument, 0, 'l'},
494 {"stat-remote-fs", no_argument, 0, 'L'},
495 {"mountpoint", no_argument, 0, 'M'},
496 {"errors-only", no_argument, 0, 'e'},
497 {"exact-match", no_argument, 0, 'E'},
498 {"all", no_argument, 0, 'A'},
499 {"verbose", no_argument, 0, 'v'},
500 {"quiet", no_argument, 0, 'q'},
501 {"clear", no_argument, 0, 'C'},
502 {"version", no_argument, 0, 'V'},
503 {"help", no_argument, 0, 'h'},
504 {0, 0, 0, 0}
505 };
507 if (argc < 2)
508 return ERROR;
510 np_add_name(&fs_exclude_list, "iso9660");
512 for (c = 1; c < argc; c++)
513 if (strcmp ("-to", argv[c]) == 0)
514 strcpy (argv[c], "-t");
516 while (1) {
517 c = getopt_long (argc, argv, "+?VqhveCt:c:w:K:W:u:p:x:X:mklLg:R:r:i:I:MEA", longopts, &option);
519 if (c == -1 || c == EOF)
520 break;
522 switch (c) {
523 case 't': /* timeout period */
524 if (is_integer (optarg)) {
525 timeout_interval = atoi (optarg);
526 break;
527 }
528 else {
529 usage2 (_("Timeout interval must be a positive integer"), optarg);
530 }
532 /* See comments for 'c' */
533 case 'w': /* warning threshold */
534 if (strstr(optarg, "%")) {
535 if (*optarg == '@') {
536 warn_freespace_percent = optarg;
537 } else {
538 asprintf(&warn_freespace_percent, "@%s", optarg);
539 }
540 } else {
541 if (*optarg == '@') {
542 warn_freespace_units = optarg;
543 } else {
544 asprintf(&warn_freespace_units, "@%s", optarg);
545 }
546 }
547 break;
549 /* Awful mistake where the range values do not make sense. Normally,
550 you alert if the value is within the range, but since we are using
551 freespace, we have to alert if outside the range. Thus we artifically
552 force @ at the beginning of the range, so that it is backwards compatible
553 */
554 case 'c': /* critical threshold */
555 if (strstr(optarg, "%")) {
556 if (*optarg == '@') {
557 crit_freespace_percent = optarg;
558 } else {
559 asprintf(&crit_freespace_percent, "@%s", optarg);
560 }
561 } else {
562 if (*optarg == '@') {
563 crit_freespace_units = optarg;
564 } else {
565 asprintf(&crit_freespace_units, "@%s", optarg);
566 }
567 }
568 break;
570 case 'W': /* warning inode threshold */
571 if (*optarg == '@') {
572 warn_freeinodes_percent = optarg;
573 } else {
574 asprintf(&warn_freeinodes_percent, "@%s", optarg);
575 }
576 break;
577 case 'K': /* critical inode threshold */
578 if (*optarg == '@') {
579 crit_freeinodes_percent = optarg;
580 } else {
581 asprintf(&crit_freeinodes_percent, "@%s", optarg);
582 }
583 break;
584 case 'u':
585 if (units)
586 free(units);
587 if (! strcmp (optarg, "bytes")) {
588 mult = (uintmax_t)1;
589 units = strdup ("B");
590 } else if (! strcmp (optarg, "kB")) {
591 mult = (uintmax_t)1024;
592 units = strdup ("kB");
593 } else if (! strcmp (optarg, "MB")) {
594 mult = (uintmax_t)1024 * 1024;
595 units = strdup ("MB");
596 } else if (! strcmp (optarg, "GB")) {
597 mult = (uintmax_t)1024 * 1024 * 1024;
598 units = strdup ("GB");
599 } else if (! strcmp (optarg, "TB")) {
600 mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
601 units = strdup ("TB");
602 } else {
603 die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
604 }
605 if (units == NULL)
606 die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
607 break;
608 case 'k': /* display mountpoint */
609 mult = 1024;
610 if (units)
611 free(units);
612 units = strdup ("kB");
613 break;
614 case 'm': /* display mountpoint */
615 mult = 1024 * 1024;
616 if (units)
617 free(units);
618 units = strdup ("MB");
619 break;
620 case 'L':
621 stat_remote_fs = 1;
622 case 'l':
623 show_local_fs = 1;
624 break;
625 case 'p': /* select path */
626 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
627 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
628 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
629 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
630 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
631 }
633 /* add parameter if not found. overwrite thresholds if path has already been added */
634 if (! (se = np_find_parameter(path_select_list, optarg))) {
635 se = np_add_parameter(&path_select_list, optarg);
636 }
637 se->group = group;
638 set_all_thresholds(se);
640 /* With autofs, it is required to stat() the path before populating the mount_list */
641 stat_path(se);
642 mount_list = read_file_system_list (0);
643 np_set_best_match(se, mount_list, exact_match);
645 path_selected = TRUE;
646 break;
647 case 'x': /* exclude path or partition */
648 np_add_name(&dp_exclude_list, optarg);
649 break;
650 case 'X': /* exclude file system type */
651 np_add_name(&fs_exclude_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 pathes\n"));
666 exact_match = TRUE;
667 break;
668 case 'g':
669 if (path_selected)
670 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting pathes \n"));
671 group = optarg;
672 break;
673 case 'I':
674 cflags |= REG_ICASE;
675 case 'i':
676 if (!path_selected)
677 die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Pathes need to be selected before using -i/-I. Use -A to select all pathes explicitly"));
678 err = regcomp(&re, optarg, cflags);
679 if (err != 0) {
680 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
681 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
682 }
684 temp_list = path_select_list;
686 previous = NULL;
687 while (temp_list) {
688 if (temp_list->best_match) {
689 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
691 if (verbose >=3)
692 printf("ignoring %s matching regex\n", temp_list->name);
694 temp_list = np_del_parameter(temp_list, previous);
695 /* pointer to first element needs to be updated if first item gets deleted */
696 if (previous == NULL)
697 path_select_list = temp_list;
698 } else {
699 previous = temp_list;
700 temp_list = temp_list->name_next;
701 }
702 } else {
703 previous = temp_list;
704 temp_list = temp_list->name_next;
705 }
706 }
709 cflags = default_cflags;
710 break;
712 case 'A':
713 optarg = strdup(".*");
714 case 'R':
715 cflags |= REG_ICASE;
716 case 'r':
717 if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
718 crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
719 warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
720 crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
721 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n"));
722 }
724 err = regcomp(&re, optarg, cflags);
725 if (err != 0) {
726 regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
727 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
728 }
730 for (me = mount_list; me; me = me->me_next) {
731 if (np_regex_match_mount_entry(me, &re)) {
732 fnd = TRUE;
733 if (verbose >= 3)
734 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
736 /* add parameter if not found. overwrite thresholds if path has already been added */
737 if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
738 se = np_add_parameter(&path_select_list, me->me_mountdir);
739 }
740 se->group = group;
741 set_all_thresholds(se);
742 }
743 }
745 if (!fnd)
746 die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
747 _("Regular expression did not match any path or disk"), optarg);
749 fnd = FALSE;
750 path_selected = TRUE;
751 np_set_best_match(path_select_list, mount_list, exact_match);
752 cflags = default_cflags;
754 break;
755 case 'M': /* display mountpoint */
756 display_mntp = TRUE;
757 break;
758 case 'C':
759 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
760 if (path_selected == FALSE) {
761 struct parameter_list *path;
762 for (me = mount_list; me; me = me->me_next) {
763 if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
764 path = np_add_parameter(&path_select_list, me->me_mountdir);
765 path->best_match = me;
766 path->group = group;
767 set_all_thresholds(path);
768 }
769 }
770 warn_freespace_units = NULL;
771 crit_freespace_units = NULL;
772 warn_usedspace_units = NULL;
773 crit_usedspace_units = NULL;
774 warn_freespace_percent = NULL;
775 crit_freespace_percent = NULL;
776 warn_usedspace_percent = NULL;
777 crit_usedspace_percent = NULL;
778 warn_usedinodes_percent = NULL;
779 crit_usedinodes_percent = NULL;
780 warn_freeinodes_percent = NULL;
781 crit_freeinodes_percent = NULL;
783 path_selected = FALSE;
784 group = NULL;
785 break;
786 case 'V': /* version */
787 print_revision (progname, revision);
788 exit (STATE_OK);
789 case 'h': /* help */
790 print_help ();
791 exit (STATE_OK);
792 case '?': /* help */
793 usage (_("Unknown argument"));
794 }
795 }
797 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
798 c = optind;
799 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
800 warn_usedspace_percent = argv[c++];
802 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
803 crit_usedspace_percent = argv[c++];
805 if (argc > c && path == NULL) {
806 se = np_add_parameter(&path_select_list, strdup(argv[c++]));
807 path_selected = TRUE;
808 set_all_thresholds(se);
809 }
811 if (units == NULL) {
812 units = strdup ("MB");
813 mult = (uintmax_t)1024 * 1024;
814 }
816 return TRUE;
817 }
821 void
822 print_path (const char *mypath)
823 {
824 if (mypath == NULL)
825 printf ("\n");
826 else
827 printf (_(" for %s\n"), mypath);
828 }
831 void
832 set_all_thresholds (struct parameter_list *path)
833 {
834 if (path->freespace_units != NULL) free(path->freespace_units);
835 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
836 if (path->freespace_percent != NULL) free (path->freespace_percent);
837 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
838 if (path->usedspace_units != NULL) free (path->usedspace_units);
839 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
840 if (path->usedspace_percent != NULL) free (path->usedspace_percent);
841 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
842 if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
843 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
844 if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
845 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
846 }
848 /* TODO: Remove?
850 int
851 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
852 {
853 if (w < 0 && c < 0 && wp < 0.0 && cp < 0.0) {
854 printf (_("INPUT ERROR: No thresholds specified"));
855 print_path (mypath);
856 return ERROR;
857 }
858 else if ((wp >= 0.0 || cp >= 0.0) &&
859 (wp < 0.0 || cp < 0.0 || wp > 100.0 || cp > 100.0 || cp > wp)) {
860 printf (_("\
861 INPUT ERROR: C_DFP (%f) should be less than W_DFP (%.1f) and both should be between zero and 100 percent, inclusive"),
862 cp, wp);
863 print_path (mypath);
864 return ERROR;
865 }
866 else if ((iwp >= 0.0 || icp >= 0.0) &&
867 (iwp < 0.0 || icp < 0.0 || iwp > 100.0 || icp > 100.0 || icp > iwp)) {
868 printf (_("\
869 INPUT ERROR: C_IDFP (%f) should be less than W_IDFP (%.1f) and both should be between zero and 100 percent, inclusive"),
870 icp, iwp);
871 print_path (mypath);
872 return ERROR;
873 }
874 else if ((w > 0 || c > 0) && (w == 0 || c == 0 || c > w)) {
875 printf (_("\
876 INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greater than zero"),
877 (unsigned long)c, (unsigned long)w);
878 print_path (mypath);
879 return ERROR;
880 }
882 return OK;
883 }
885 */
893 void
894 print_help (void)
895 {
896 print_revision (progname, revision);
898 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
899 printf (COPYRIGHT, copyright, email);
901 printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
902 printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
904 printf ("\n\n");
906 print_usage ();
908 printf (_(UT_HELP_VRSN));
909 printf (_(UT_EXTRA_OPTS));
911 printf (" %s\n", "-w, --warning=INTEGER");
912 printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
913 printf (" %s\n", "-w, --warning=PERCENT%");
914 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
915 printf (" %s\n", "-c, --critical=INTEGER");
916 printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
917 printf (" %s\n", "-c, --critical=PERCENT%");
918 printf (" %s\n", _("Exit with CRITCAL status if less than PERCENT of disk space is free"));
919 printf (" %s\n", "-W, --iwarning=PERCENT%");
920 printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
921 printf (" %s\n", "-K, --icritical=PERCENT%");
922 printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
923 printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
924 printf (" %s\n", _("Path or partition (may be repeated)"));
925 printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
926 printf (" %s\n", _("Ignore device (only works if -p unspecified)"));
927 printf (" %s\n", "-C, --clear");
928 printf (" %s\n", _("Clear thresholds"));
929 printf (" %s\n", "-E, --exact-match");
930 printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
931 printf (" %s\n", "-e, --errors-only");
932 printf (" %s\n", _("Display only devices/mountpoints with errors"));
933 printf (" %s\n", "-g, --group=NAME");
934 printf (" %s\n", _("Group pathes. Thresholds apply to (free-)space of all partitions together"));
935 printf (" %s\n", "-k, --kilobytes");
936 printf (" %s\n", _("Same as '--units kB'"));
937 printf (" %s\n", "-l, --local");
938 printf (" %s\n", _("Only check local filesystems"));
939 printf (" %s\n", "-L, --stat-remote-fs");
940 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
941 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
942 printf (" %s\n", "-M, --mountpoint");
943 printf (" %s\n", _("Display the mountpoint instead of the partition"));
944 printf (" %s\n", "-m, --megabytes");
945 printf (" %s\n", _("Same as '--units MB'"));
946 printf (" %s\n", "-A, --all");
947 printf (" %s\n", _("Explicitly select all pathes. This is equivalent to -R '.*'"));
948 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
949 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
950 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
951 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
952 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
953 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
954 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
955 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
956 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
957 printf (" %s\n", "-u, --units=STRING");
958 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
959 printf (_(UT_VERBOSE));
960 printf (" %s\n", "-X, --exclude-type=TYPE");
961 printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
963 #ifdef NP_EXTRA_OPTS
964 printf ("\n");
965 printf ("%s\n", _("Notes:"));
966 printf (_(UT_EXTRA_OPTS_NOTES));
967 #endif
969 printf ("\n");
970 printf ("%s\n", _("Examples:"));
971 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
972 printf (" %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
973 printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -g sidDATA -r '^/oracle/SID/data.*$'");
974 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
975 printf (" %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
976 printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -p /foo -C -w 5% -c 3% -p /bar");
977 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
979 printf (_(UT_SUPPORT));
980 }
984 void
985 print_usage (void)
986 {
987 printf (_("Usage:"));
988 printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
989 printf ("[-C] [-E] [-e] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
990 printf ("[-t timeout] [-u unit] [-v] [-X type]\n");
991 }
993 void
994 stat_path (struct parameter_list *p)
995 {
996 /* Stat entry to check that dir exists and is accessible */
997 if (verbose >= 3)
998 printf("calling stat on %s\n", p->name);
999 if (stat (p->name, &stat_buf[0])) {
1000 if (verbose >= 3)
1001 printf("stat failed on %s\n", p->name);
1002 printf("DISK %s - ", _("CRITICAL"));
1003 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
1004 }
1005 }