Code

Fix check_disk free space calculation if blocksizes differ within a disk group (Bekar...
[nagiosplug.git] / plugins / check_disk.c
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
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)
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;
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;
391 /* process command-line arguments */
392 int
393 process_arguments (int argc, char **argv)
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;
762 void
763 print_path (const char *mypath)
765   if (mypath == NULL)
766     printf ("\n");
767   else
768     printf (_(" for %s\n"), mypath);
772 void
773 set_all_thresholds (struct parameter_list *path)
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);
789 /* TODO: Remove?
791 int
792 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
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;
826 */
834 void
835 print_help (void)
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);
919 void
920 print_usage (void)
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");
928 void
929 stat_path (struct parameter_list *p)
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   }
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;
970             
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;
992   
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;
1004   
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);