Code

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