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 re-populating the mount_list */
641 stat_path(se);
642 /* NB: We can't free the old mount_list "just like that": both list pointers and struct
643 * pointers are copied around. One of the reason it wasn't done yet is that other parts
644 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
645 mount_list = read_file_system_list (0);
646 np_set_best_match(se, mount_list, exact_match);
648 path_selected = TRUE;
649 break;
650 case 'x': /* exclude path or partition */
651 np_add_name(&dp_exclude_list, optarg);
652 break;
653 case 'X': /* exclude file system type */
654 np_add_name(&fs_exclude_list, optarg);
655 break;
656 case 'v': /* verbose */
657 verbose++;
658 break;
659 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
660 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
661 erronly = TRUE;
662 break;
663 case 'e':
664 erronly = TRUE;
665 break;
666 case 'E':
667 if (path_selected)
668 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting pathes\n"));
669 exact_match = TRUE;
670 break;
671 case 'g':
672 if (path_selected)
673 die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting pathes \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"), _("Pathes need to be selected before using -i/-I. Use -A to select all pathes 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);
685 }
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;
704 }
705 } else {
706 previous = temp_list;
707 temp_list = temp_list->name_next;
708 }
709 }
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"));
725 }
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);
731 }
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);
742 }
743 se->group = group;
744 set_all_thresholds(se);
745 }
746 }
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);
771 }
772 }
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, revision);
791 exit (STATE_OK);
792 case 'h': /* help */
793 print_help ();
794 exit (STATE_OK);
795 case '?': /* help */
796 usage (_("Unknown argument"));
797 }
798 }
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);
812 }
814 if (units == NULL) {
815 units = strdup ("MB");
816 mult = (uintmax_t)1024 * 1024;
817 }
819 return TRUE;
820 }
824 void
825 print_path (const char *mypath)
826 {
827 if (mypath == NULL)
828 printf ("\n");
829 else
830 printf (_(" for %s\n"), mypath);
831 }
834 void
835 set_all_thresholds (struct parameter_list *path)
836 {
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);
849 }
851 /* TODO: Remove?
853 int
854 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
855 {
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;
860 }
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;
868 }
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;
876 }
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;
883 }
885 return OK;
886 }
888 */
896 void
897 print_help (void)
898 {
899 print_revision (progname, revision);
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 CRITCAL 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", _("Path or partition (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", "-g, --group=NAME");
937 printf (" %s\n", _("Group pathes. Thresholds apply to (free-)space of all partitions together"));
938 printf (" %s\n", "-k, --kilobytes");
939 printf (" %s\n", _("Same as '--units kB'"));
940 printf (" %s\n", "-l, --local");
941 printf (" %s\n", _("Only check local filesystems"));
942 printf (" %s\n", "-L, --stat-remote-fs");
943 printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
944 printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
945 printf (" %s\n", "-M, --mountpoint");
946 printf (" %s\n", _("Display the mountpoint instead of the partition"));
947 printf (" %s\n", "-m, --megabytes");
948 printf (" %s\n", _("Same as '--units MB'"));
949 printf (" %s\n", "-A, --all");
950 printf (" %s\n", _("Explicitly select all pathes. This is equivalent to -R '.*'"));
951 printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
952 printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
953 printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
954 printf (" %s\n", _("Regular expression for path or partition (may be repeated)"));
955 printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
956 printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
957 printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
958 printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
959 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
960 printf (" %s\n", "-u, --units=STRING");
961 printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
962 printf (_(UT_VERBOSE));
963 printf (" %s\n", "-X, --exclude-type=TYPE");
964 printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
966 #ifdef NP_EXTRA_OPTS
967 printf ("\n");
968 printf ("%s\n", _("Notes:"));
969 printf (_(UT_EXTRA_OPTS_NOTES));
970 #endif
972 printf ("\n");
973 printf ("%s\n", _("Examples:"));
974 printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
975 printf (" %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
976 printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -g sidDATA -r '^/oracle/SID/data.*$'");
977 printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
978 printf (" %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
979 printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -p /foo -C -w 5% -c 3% -p /bar");
980 printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
982 printf (_(UT_SUPPORT));
983 }
987 void
988 print_usage (void)
989 {
990 printf (_("Usage:"));
991 printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
992 printf ("[-C] [-E] [-e] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
993 printf ("[-t timeout] [-u unit] [-v] [-X type]\n");
994 }
996 void
997 stat_path (struct parameter_list *p)
998 {
999 /* Stat entry to check that dir exists and is accessible */
1000 if (verbose >= 3)
1001 printf("calling stat on %s\n", p->name);
1002 if (stat (p->name, &stat_buf[0])) {
1003 if (verbose >= 3)
1004 printf("stat failed on %s\n", p->name);
1005 printf("DISK %s - ", _("CRITICAL"));
1006 die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
1007 }
1008 }