Code

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