Code

12b77a8ab8b16d7d36d446f548fc2326d1a52403
[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 int process_arguments (int, char **);
122 void print_path (const char *mypath);
123 void set_all_thresholds (struct parameter_list *path);
124 int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *);
125 void print_help (void);
126 void print_usage (void);
127 double calculate_percent(uintmax_t, uintmax_t);
128 void stat_path (struct parameter_list *p);
130 double w_dfp = -1.0;
131 double c_dfp = -1.0;
132 char *path;
133 char *exclude_device;
134 char *units;
135 uintmax_t mult = 1024 * 1024;
136 int verbose = 0;
137 int erronly = FALSE;
138 int display_mntp = FALSE;
139 int exact_match = FALSE;
140 char *warn_freespace_units = NULL;
141 char *crit_freespace_units = NULL;
142 char *warn_freespace_percent = NULL;
143 char *crit_freespace_percent = NULL;
144 char *warn_usedspace_units = NULL;
145 char *crit_usedspace_units = NULL;
146 char *warn_usedspace_percent = NULL;
147 char *crit_usedspace_percent = NULL;
148 char *warn_usedinodes_percent = NULL;
149 char *crit_usedinodes_percent = NULL;
150 char *warn_freeinodes_percent = NULL;
151 char *crit_freeinodes_percent = NULL;
152 int path_selected = FALSE;
153 char *group = NULL;
154 struct stat *stat_buf;
157 int
158 main (int argc, char **argv)
160   int result = STATE_UNKNOWN;
161   int disk_result = STATE_UNKNOWN;
162   char *output;
163   char *details;
164   char *perf;
165   char *preamble;
166   double inode_space_pct;
167   uintmax_t total, available, available_to_root, used;
168   double dfree_pct = -1, dused_pct = -1;
169   double dused_units, dfree_units, dtotal_units;
170   double dused_inodes_percent, dfree_inodes_percent;
171   double warning_high_tide;
172   double critical_high_tide;
173   int temp_result;
175   struct mount_entry *me;
176   struct fs_usage fsp, tmpfsp;
177   struct parameter_list *temp_list, *path;
178   struct name_list *seen = NULL;
180   preamble = strdup (" - free space:");
181   output = strdup ("");
182   details = strdup ("");
183   perf = strdup ("");
184   stat_buf = malloc(sizeof *stat_buf);
186   setlocale (LC_ALL, "");
187   bindtextdomain (PACKAGE, LOCALEDIR);
188   textdomain (PACKAGE);
190   mount_list = read_file_system_list (0);
192   /* Parse extra opts if any */
193   argv = np_extra_opts (&argc, argv, progname);
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   }
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))) {
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. */
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 */
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     {"kilobytes", no_argument, 0, 'k'},
478     {"megabytes", no_argument, 0, 'm'},
479     {"units", required_argument, 0, 'u'},
480     {"path", required_argument, 0, 'p'},
481     {"partition", required_argument, 0, 'p'},
482     {"exclude_device", required_argument, 0, 'x'},
483     {"exclude-type", required_argument, 0, 'X'},
484     {"group", required_argument, 0, 'g'},
485     {"eregi-path", required_argument, 0, 'R'},
486     {"eregi-partition", required_argument, 0, 'R'},
487     {"ereg-path", required_argument, 0, 'r'},
488     {"ereg-partition", required_argument, 0, 'r'},
489     {"ignore-ereg-path", required_argument, 0, 'i'},
490     {"ignore-ereg-partition", required_argument, 0, 'i'},
491     {"ignore-eregi-path", required_argument, 0, 'I'},
492     {"ignore-eregi-partition", required_argument, 0, 'I'},
493     {"local", no_argument, 0, 'l'},
494     {"stat-remote-fs", no_argument, 0, 'L'},
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);
640       /* With autofs, it is required to stat() the path before re-populating the mount_list */
641       stat_path(se);
642       /* NB: We can't free the old mount_list "just like that": both list pointers and struct
643        * pointers are copied around. One of the reason it wasn't done yet is that other parts
644        * of check_disk need the same kind of cleanup so it'd better be done as a whole */
645       mount_list = read_file_system_list (0);
646       np_set_best_match(se, mount_list, exact_match);
648       path_selected = TRUE;
649       break;
650     case 'x':                 /* exclude path or partition */
651       np_add_name(&dp_exclude_list, optarg);
652       break;
653     case 'X':                 /* exclude file system type */
654       np_add_name(&fs_exclude_list, optarg);
655       break;
656     case 'v':                 /* verbose */
657       verbose++;
658       break;
659     case 'q':                 /* TODO: this function should eventually go away (removed 2007-09-20) */
660       /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
661       erronly = TRUE;
662       break;
663     case 'e':
664       erronly = TRUE;
665       break;
666     case 'E':
667       if (path_selected)
668         die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting pathes\n"));
669       exact_match = TRUE;
670       break;
671     case 'g':
672       if (path_selected)
673         die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting pathes \n"));
674       group = optarg;
675       break;
676     case 'I':
677       cflags |= REG_ICASE;
678     case 'i':
679       if (!path_selected)
680         die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Pathes need to be selected before using -i/-I. Use -A to select all pathes explicitly"));
681       err = regcomp(&re, optarg, cflags);
682       if (err != 0) {
683         regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
684         die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
685       }
687       temp_list = path_select_list;
689       previous = NULL;
690       while (temp_list) {
691         if (temp_list->best_match) {
692           if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
694               if (verbose >=3)
695                 printf("ignoring %s matching regex\n", temp_list->name);
697               temp_list = np_del_parameter(temp_list, previous);
698               /* pointer to first element needs to be updated if first item gets deleted */
699               if (previous == NULL)
700                 path_select_list = temp_list;
701           } else {
702             previous = temp_list;
703             temp_list = temp_list->name_next;
704           }
705         } else {
706           previous = temp_list;
707           temp_list = temp_list->name_next;
708         }
709       }
712       cflags = default_cflags;
713       break;
715     case 'A':
716       optarg = strdup(".*");
717     case 'R':
718       cflags |= REG_ICASE;
719     case 'r':
720       if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
721              crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
722              warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
723              crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
724         die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n"));
725       }
727       err = regcomp(&re, optarg, cflags);
728       if (err != 0) {
729         regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
730         die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
731       }
733       for (me = mount_list; me; me = me->me_next) {
734         if (np_regex_match_mount_entry(me, &re)) {
735           fnd = TRUE;
736           if (verbose >= 3)
737             printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
739           /* add parameter if not found. overwrite thresholds if path has already been added  */
740           if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
741             se = np_add_parameter(&path_select_list, me->me_mountdir);
742           }
743           se->group = group;
744           set_all_thresholds(se);
745         }
746       }
748       if (!fnd)
749         die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
750             _("Regular expression did not match any path or disk"), optarg);
752       fnd = FALSE;
753       path_selected = TRUE;
754       np_set_best_match(path_select_list, mount_list, exact_match);
755       cflags = default_cflags;
757       break;
758     case 'M': /* display mountpoint */
759       display_mntp = TRUE;
760       break;
761     case 'C':
762        /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
763        if (path_selected == FALSE) {
764          struct parameter_list *path;
765          for (me = mount_list; me; me = me->me_next) {
766            if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
767              path = np_add_parameter(&path_select_list, me->me_mountdir);
768            path->best_match = me;
769            path->group = group;
770            set_all_thresholds(path);
771          }
772       }
773       warn_freespace_units = NULL;
774       crit_freespace_units = NULL;
775       warn_usedspace_units = NULL;
776       crit_usedspace_units = NULL;
777       warn_freespace_percent = NULL;
778       crit_freespace_percent = NULL;
779       warn_usedspace_percent = NULL;
780       crit_usedspace_percent = NULL;
781       warn_usedinodes_percent = NULL;
782       crit_usedinodes_percent = NULL;
783       warn_freeinodes_percent = NULL;
784       crit_freeinodes_percent = NULL;
786       path_selected = FALSE;
787       group = NULL;
788       break;
789     case 'V':                 /* version */
790       print_revision (progname, revision);
791       exit (STATE_OK);
792     case 'h':                 /* help */
793       print_help ();
794       exit (STATE_OK);
795     case '?':                 /* help */
796       usage (_("Unknown argument"));
797     }
798   }
800   /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
801   c = optind;
802   if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
803     warn_usedspace_percent = argv[c++];
805   if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
806     crit_usedspace_percent = argv[c++];
808   if (argc > c && path == NULL) {
809     se = np_add_parameter(&path_select_list, strdup(argv[c++]));
810     path_selected = TRUE;
811     set_all_thresholds(se);
812   }
814   if (units == NULL) {
815     units = strdup ("MB");
816     mult = (uintmax_t)1024 * 1024;
817   }
819   return TRUE;
824 void
825 print_path (const char *mypath)
827   if (mypath == NULL)
828     printf ("\n");
829   else
830     printf (_(" for %s\n"), mypath);
834 void
835 set_all_thresholds (struct parameter_list *path)
837     if (path->freespace_units != NULL) free(path->freespace_units);
838     set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
839     if (path->freespace_percent != NULL) free (path->freespace_percent);
840     set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
841     if (path->usedspace_units != NULL) free (path->usedspace_units);
842     set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
843     if (path->usedspace_percent != NULL) free (path->usedspace_percent);
844     set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
845     if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
846     set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
847     if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
848     set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
851 /* TODO: Remove?
853 int
854 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
856   if (w < 0 && c < 0 && wp < 0.0 && cp < 0.0) {
857     printf (_("INPUT ERROR: No thresholds specified"));
858     print_path (mypath);
859     return ERROR;
860   }
861   else if ((wp >= 0.0 || cp >= 0.0) &&
862            (wp < 0.0 || cp < 0.0 || wp > 100.0 || cp > 100.0 || cp > wp)) {
863     printf (_("\
864 INPUT ERROR: C_DFP (%f) should be less than W_DFP (%.1f) and both should be between zero and 100 percent, inclusive"),
865             cp, wp);
866     print_path (mypath);
867     return ERROR;
868   }
869   else if ((iwp >= 0.0 || icp >= 0.0) &&
870            (iwp < 0.0 || icp < 0.0 || iwp > 100.0 || icp > 100.0 || icp > iwp)) {
871     printf (_("\
872 INPUT ERROR: C_IDFP (%f) should be less than W_IDFP (%.1f) and both should be between zero and 100 percent, inclusive"),
873             icp, iwp);
874     print_path (mypath);
875     return ERROR;
876   }
877   else if ((w > 0 || c > 0) && (w == 0 || c == 0 || c > w)) {
878     printf (_("\
879 INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greater than zero"),
880             (unsigned long)c, (unsigned long)w);
881     print_path (mypath);
882     return ERROR;
883   }
885   return OK;
888 */
896 void
897 print_help (void)
899   print_revision (progname, revision);
901   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
902   printf (COPYRIGHT, copyright, email);
904   printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
905   printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
907   printf ("\n\n");
909   print_usage ();
911   printf (_(UT_HELP_VRSN));
912   printf (_(UT_EXTRA_OPTS));
914   printf (" %s\n", "-w, --warning=INTEGER");
915   printf ("    %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
916   printf (" %s\n", "-w, --warning=PERCENT%");
917   printf ("    %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
918   printf (" %s\n", "-c, --critical=INTEGER");
919   printf ("    %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
920   printf (" %s\n", "-c, --critical=PERCENT%");
921   printf ("    %s\n", _("Exit with CRITCAL status if less than PERCENT of disk space is free"));
922   printf (" %s\n", "-W, --iwarning=PERCENT%");
923   printf ("    %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
924   printf (" %s\n", "-K, --icritical=PERCENT%");
925   printf ("    %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
926   printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
927   printf ("    %s\n", _("Path or partition (may be repeated)"));
928   printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
929   printf ("    %s\n", _("Ignore device (only works if -p unspecified)"));
930   printf (" %s\n", "-C, --clear");
931   printf ("    %s\n", _("Clear thresholds"));
932   printf (" %s\n", "-E, --exact-match");
933   printf ("    %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
934   printf (" %s\n", "-e, --errors-only");
935   printf ("    %s\n", _("Display only devices/mountpoints with errors"));
936   printf (" %s\n", "-g, --group=NAME");
937   printf ("    %s\n", _("Group pathes. Thresholds apply to (free-)space of all partitions together"));
938   printf (" %s\n", "-k, --kilobytes");
939   printf ("    %s\n", _("Same as '--units kB'"));
940   printf (" %s\n", "-l, --local");
941   printf ("    %s\n", _("Only check local filesystems"));
942   printf (" %s\n", "-L, --stat-remote-fs");
943   printf ("    %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
944   printf ("    %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
945   printf (" %s\n", "-M, --mountpoint");
946   printf ("    %s\n", _("Display the mountpoint instead of the partition"));
947   printf (" %s\n", "-m, --megabytes");
948   printf ("    %s\n", _("Same as '--units MB'"));
949   printf (" %s\n", "-A, --all");
950   printf ("    %s\n", _("Explicitly select all pathes. This is equivalent to -R '.*'"));
951   printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
952   printf ("    %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
953   printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
954   printf ("    %s\n", _("Regular expression for path or partition (may be repeated)"));
955   printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
956   printf ("    %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
957   printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
958   printf ("    %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
959   printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
960   printf (" %s\n", "-u, --units=STRING");
961   printf ("    %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
962   printf (_(UT_VERBOSE));
963   printf (" %s\n", "-X, --exclude-type=TYPE");
964   printf ("    %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
966 #ifdef NP_EXTRA_OPTS
967   printf ("\n");
968   printf ("%s\n", _("Notes:"));
969   printf (_(UT_EXTRA_OPTS_NOTES));
970 #endif
972   printf ("\n");
973   printf ("%s\n", _("Examples:"));
974   printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
975   printf ("    %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
976   printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -g sidDATA -r '^/oracle/SID/data.*$'");
977   printf ("    %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
978   printf ("    %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
979   printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -p /foo -C -w 5% -c 3% -p /bar");
980   printf ("    %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
982   printf (_(UT_SUPPORT));
987 void
988 print_usage (void)
990   printf (_("Usage:"));
991   printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
992   printf ("[-C] [-E] [-e] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
993   printf ("[-t timeout] [-u unit] [-v] [-X type]\n");
996 void
997 stat_path (struct parameter_list *p)
999   /* Stat entry to check that dir exists and is accessible */
1000   if (verbose >= 3)
1001     printf("calling stat on %s\n", p->name);
1002   if (stat (p->name, &stat_buf[0])) {
1003     if (verbose >= 3)
1004       printf("stat failed on %s\n", p->name);
1005     printf("DISK %s - ", _("CRITICAL"));
1006     die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
1007   }