Code

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