Code

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