Back to index

nagios-plugins  1.4.16
check_disk.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002 * 
00003 * Nagios check_disk plugin
00004 * 
00005 * License: GPL
00006 * Copyright (c) 1999-2008 Nagios Plugins Development Team
00007 * 
00008 * Description:
00009 * 
00010 * This file contains the check_disk plugin
00011 * 
00012 * 
00013 * This program is free software: you can redistribute it and/or modify
00014 * it under the terms of the GNU General Public License as published by
00015 * the Free Software Foundation, either version 3 of the License, or
00016 * (at your option) any later version.
00017 * 
00018 * This program is distributed in the hope that it will be useful,
00019 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021 * GNU General Public License for more details.
00022 * 
00023 * You should have received a copy of the GNU General Public License
00024 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00025 * 
00026 * 
00027 *****************************************************************************/
00028 
00029 const char *progname = "check_disk";
00030 const char *program_name = "check_disk";  /* Required for coreutils libs */
00031 const char *copyright = "1999-2008";
00032 const char *email = "nagiosplug-devel@lists.sourceforge.net";
00033 
00034 
00035 #include "common.h"
00036 #ifdef HAVE_SYS_STAT_H
00037 # include <sys/stat.h>
00038 #endif
00039 #if HAVE_INTTYPES_H
00040 # include <inttypes.h>
00041 #endif
00042 #include <assert.h>
00043 #include "popen.h"
00044 #include "utils.h"
00045 #include "utils_disk.h"
00046 #include <stdarg.h>
00047 #include "fsusage.h"
00048 #include "mountlist.h"
00049 #include "intprops.h"       /* necessary for TYPE_MAXIMUM */
00050 #if HAVE_LIMITS_H
00051 # include <limits.h>
00052 #endif
00053 #include "regex.h"
00054 
00055 
00056 /* If nonzero, show inode information. */
00057 static int inode_format = 1;
00058 
00059 /* If nonzero, show even filesystems with zero size or
00060    uninteresting types. */
00061 static int show_all_fs = 1;
00062 
00063 /* If nonzero, show only local filesystems.  */
00064 static int show_local_fs = 0;
00065 
00066 /* If nonzero, show only local filesystems but call stat() on remote ones. */
00067 static int stat_remote_fs = 0;
00068 
00069 /* If positive, the units to use when printing sizes;
00070    if negative, the human-readable base.  */
00071 /* static int output_block_size; */
00072 
00073 /* If nonzero, invoke the `sync' system call before getting any usage data.
00074    Using this option can make df very slow, especially with many or very
00075    busy disks.  Note that this may make a difference on some systems --
00076    SunOs4.1.3, for one.  It is *not* necessary on Linux.  */
00077 /* static int require_sync = 0; */
00078 
00079 /* Linked list of filesystem types to display.
00080    If `fs_select_list' is NULL, list all types.
00081    This table is generated dynamically from command-line options,
00082    rather than hardcoding into the program what it thinks are the
00083    valid filesystem types; let the user specify any filesystem type
00084    they want to, and if there are any filesystems of that type, they
00085    will be shown.
00086 
00087    Some filesystem types:
00088    4.2 4.3 ufs nfs swap ignore io vm efs dbg */
00089 
00090 /* static struct parameter_list *fs_select_list; */
00091 
00092 /* Linked list of filesystem types to omit.
00093    If the list is empty, don't exclude any types.  */
00094 
00095 static struct name_list *fs_exclude_list;
00096 
00097 static struct name_list *dp_exclude_list;
00098 
00099 static struct parameter_list *path_select_list = NULL;
00100 
00101 /* Linked list of mounted filesystems. */
00102 static struct mount_entry *mount_list;
00103 
00104 /* For long options that have no equivalent short option, use a
00105    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
00106 enum
00107 {
00108   SYNC_OPTION = CHAR_MAX + 1,
00109   NO_SYNC_OPTION,
00110   BLOCK_SIZE_OPTION
00111 };
00112 
00113 #ifdef _AIX
00114  #pragma alloca
00115 #endif
00116 
00117 int process_arguments (int, char **);
00118 void print_path (const char *mypath);
00119 void set_all_thresholds (struct parameter_list *path);
00120 int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *);
00121 void print_help (void);
00122 void print_usage (void);
00123 double calculate_percent(uintmax_t, uintmax_t);
00124 void stat_path (struct parameter_list *p);
00125 void get_stats (struct parameter_list *p, struct fs_usage *fsp);
00126 void get_path_stats (struct parameter_list *p, struct fs_usage *fsp);
00127 
00128 double w_dfp = -1.0;
00129 double c_dfp = -1.0;
00130 char *path;
00131 char *exclude_device;
00132 char *units;
00133 uintmax_t mult = 1024 * 1024;
00134 int verbose = 0;
00135 int erronly = FALSE;
00136 int display_mntp = FALSE;
00137 int exact_match = FALSE;
00138 char *warn_freespace_units = NULL;
00139 char *crit_freespace_units = NULL;
00140 char *warn_freespace_percent = NULL;
00141 char *crit_freespace_percent = NULL;
00142 char *warn_usedspace_units = NULL;
00143 char *crit_usedspace_units = NULL;
00144 char *warn_usedspace_percent = NULL;
00145 char *crit_usedspace_percent = NULL;
00146 char *warn_usedinodes_percent = NULL;
00147 char *crit_usedinodes_percent = NULL;
00148 char *warn_freeinodes_percent = NULL;
00149 char *crit_freeinodes_percent = NULL;
00150 int path_selected = FALSE;
00151 char *group = NULL;
00152 struct stat *stat_buf;
00153 struct name_list *seen = NULL;
00154 
00155 
00156 int
00157 main (int argc, char **argv)
00158 {
00159   int result = STATE_UNKNOWN;
00160   int disk_result = STATE_UNKNOWN;
00161   char *output;
00162   char *details;
00163   char *perf;
00164   char *preamble;
00165   double inode_space_pct;
00166   double warning_high_tide;
00167   double critical_high_tide;
00168   int temp_result;
00169 
00170   struct mount_entry *me;
00171   struct fs_usage fsp, tmpfsp;
00172   struct parameter_list *temp_list, *path;
00173 
00174   preamble = strdup (" - free space:");
00175   output = strdup ("");
00176   details = strdup ("");
00177   perf = strdup ("");
00178   stat_buf = malloc(sizeof *stat_buf);
00179 
00180   setlocale (LC_ALL, "");
00181   bindtextdomain (PACKAGE, LOCALEDIR);
00182   textdomain (PACKAGE);
00183 
00184   mount_list = read_file_system_list (0);
00185 
00186   /* Parse extra opts if any */
00187   argv = np_extra_opts (&argc, argv, progname);
00188 
00189   if (process_arguments (argc, argv) == ERROR)
00190     usage4 (_("Could not parse arguments"));
00191 
00192   /* If a list of paths has not been selected, find entire
00193      mount list and create list of paths
00194    */
00195   if (path_selected == FALSE) {
00196     for (me = mount_list; me; me = me->me_next) {
00197       if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) {
00198         path = np_add_parameter(&path_select_list, me->me_mountdir);
00199       }
00200       path->best_match = me;
00201       path->group = group;
00202       set_all_thresholds(path);
00203     }
00204   }
00205   np_set_best_match(path_select_list, mount_list, exact_match);
00206 
00207   /* Error if no match found for specified paths */
00208   temp_list = path_select_list;
00209 
00210   while (temp_list) {
00211     if (! temp_list->best_match) {
00212       die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), temp_list->name);
00213     }
00214 
00215     temp_list = temp_list->name_next;
00216   }
00217 
00218   /* Process for every path in list */
00219   for (path = path_select_list; path; path=path->name_next) {
00220 
00221     if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL)
00222       printf("Thresholds(pct) for %s warn: %f crit %f\n",path->name, path->freespace_percent->warning->end,
00223                                                          path->freespace_percent->critical->end);
00224 
00225     if (verbose >= 3 && path->group != NULL)
00226       printf("Group of %s: %s\n",path->name,path->group);
00227 
00228     /* reset disk result */
00229     disk_result = STATE_UNKNOWN;
00230 
00231     me = path->best_match;
00232 
00233     /* Filters */
00234 
00235     /* Remove filesystems already seen */
00236     if (np_seen_name(seen, me->me_mountdir)) {
00237       continue;
00238     } 
00239     np_add_name(&seen, me->me_mountdir);
00240 
00241     if (path->group == NULL) {
00242       /* Skip remote filesystems if we're not interested in them */
00243       if (me->me_remote && show_local_fs) {
00244         if (stat_remote_fs)
00245           stat_path(path);
00246         continue;
00247       /* Skip pseudo fs's if we haven't asked for all fs's */
00248       } else if (me->me_dummy && !show_all_fs) {
00249         continue;
00250       /* Skip excluded fstypes */
00251       } else if (fs_exclude_list && np_find_name (fs_exclude_list, me->me_type)) {
00252         continue;
00253       /* Skip excluded fs's */
00254       } else if (dp_exclude_list &&
00255                (np_find_name (dp_exclude_list, me->me_devname) ||
00256                 np_find_name (dp_exclude_list, me->me_mountdir))) {
00257         continue;
00258       }
00259 
00260       stat_path(path);
00261       get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
00262     }
00263 
00264     if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
00265       get_stats (path, &fsp);
00266 
00267       if (verbose >= 3) {
00268         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",
00269           me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units, path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult);
00270       }
00271 
00272       /* Threshold comparisons */
00273 
00274       temp_result = get_status(path->dfree_units, path->freespace_units);
00275       if (verbose >=3) printf("Freespace_units result=%d\n", temp_result);
00276       disk_result = max_state( disk_result, temp_result );
00277 
00278       temp_result = get_status(path->dfree_pct, path->freespace_percent);
00279       if (verbose >=3) printf("Freespace%% result=%d\n", temp_result);
00280       disk_result = max_state( disk_result, temp_result );
00281 
00282       temp_result = get_status(path->dused_units, path->usedspace_units);
00283       if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result);
00284       disk_result = max_state( disk_result, temp_result );
00285 
00286       temp_result = get_status(path->dused_pct, path->usedspace_percent);
00287       if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result);
00288       disk_result = max_state( disk_result, temp_result );
00289 
00290       temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
00291       if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result);
00292       disk_result = max_state( disk_result, temp_result );
00293 
00294       temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
00295       if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result);
00296       disk_result = max_state( disk_result, temp_result );
00297 
00298       result = max_state(result, disk_result);
00299 
00300       /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
00301          Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
00302          data. Assumption that start=0. Roll on new syntax...
00303       */
00304 
00305       /* *_high_tide must be reinitialized at each run */
00306       warning_high_tide = UINT_MAX;
00307       critical_high_tide = UINT_MAX;
00308 
00309       if (path->freespace_units->warning != NULL) {
00310         warning_high_tide = path->dtotal_units - path->freespace_units->warning->end;
00311       }
00312       if (path->freespace_percent->warning != NULL) {
00313         warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freespace_percent->warning->end/100)*path->dtotal_units ));
00314       }
00315       if (path->freespace_units->critical != NULL) {
00316         critical_high_tide = path->dtotal_units - path->freespace_units->critical->end;
00317       }
00318       if (path->freespace_percent->critical != NULL) {
00319         critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freespace_percent->critical->end/100)*path->dtotal_units ));
00320       }
00321 
00322       /* Nb: *_high_tide are unset when == UINT_MAX */
00323       asprintf (&perf, "%s %s", perf,
00324                 perfdata ((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
00325                           path->dused_units, units,
00326                           (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide,
00327                           (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide,
00328                           TRUE, 0,
00329                           TRUE, path->dtotal_units));
00330 
00331       if (disk_result==STATE_OK && erronly && !verbose)
00332         continue;
00333 
00334       asprintf (&output, "%s %s %.0f %s (%.0f%%",
00335                 output,
00336                 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir,
00337                 path->dfree_units,
00338                 units,
00339                 path->dfree_pct);
00340       if (path->dused_inodes_percent < 0) {
00341         asprintf(&output, "%s inode=-);", output);
00342       } else {
00343         asprintf(&output, "%s inode=%.0f%%);", output, path->dfree_inodes_percent );
00344       }
00345 
00346       /* TODO: Need to do a similar debug line
00347       asprintf (&details, _("%s\n\
00348 %.0f of %.0f %s (%.0f%% inode=%.0f%%) free on %s (type %s mounted on %s) warn:%lu crit:%lu warn%%:%.0f%% crit%%:%.0f%%"),
00349                 details, dfree_units, dtotal_units, units, dfree_pct, inode_space_pct,
00350                 me->me_devname, me->me_type, me->me_mountdir,
00351                 (unsigned long)w_df, (unsigned long)c_df, w_dfp, c_dfp);
00352       */
00353 
00354     }
00355 
00356   }
00357 
00358   if (verbose >= 2)
00359     asprintf (&output, "%s%s", output, details);
00360 
00361 
00362   printf ("DISK %s%s%s|%s\n", state_text (result), (erronly && result==STATE_OK) ? "" : preamble, output, perf);
00363   return result;
00364 }
00365 
00366 
00367 double calculate_percent(uintmax_t value, uintmax_t total) {
00368   double pct = -1;
00369   /* I don't understand the below, but it is taken from coreutils' df */
00370   /* Seems to be calculating pct, in the best possible way */
00371   if (value <= TYPE_MAXIMUM(uintmax_t) / 100
00372     && total != 0) {
00373     uintmax_t u100 = value * 100;
00374     pct = u100 / total + (u100 % total != 0);
00375   } else {
00376     /* Possible rounding errors - see coreutils' df for more explanation */
00377     double u = value;
00378     double t = total;
00379     if (t) {
00380       long int lipct = pct = u * 100 / t;
00381       double ipct = lipct;
00382 
00383       /* Like 'pct = ceil (dpct);', but without ceil - from coreutils again */
00384       if (ipct - 1 < pct && pct <= ipct + 1)
00385         pct = ipct + (ipct < pct);
00386     }
00387   }
00388   return pct;
00389 }
00390 
00391 /* process command-line arguments */
00392 int
00393 process_arguments (int argc, char **argv)
00394 {
00395   int c, err;
00396   struct parameter_list *se;
00397   struct parameter_list *temp_list = NULL, *previous = NULL;
00398   struct parameter_list *temp_path_select_list = NULL;
00399   struct mount_entry *me, *temp_me;
00400   int result = OK;
00401   regex_t re;
00402   int cflags = REG_NOSUB | REG_EXTENDED;
00403   int default_cflags = cflags;
00404   char errbuf[MAX_INPUT_BUFFER];
00405   int fnd = 0;
00406 
00407   int option = 0;
00408   static struct option longopts[] = {
00409     {"timeout", required_argument, 0, 't'},
00410     {"warning", required_argument, 0, 'w'},
00411     {"critical", required_argument, 0, 'c'},
00412     {"iwarning", required_argument, 0, 'W'},
00413     /* Dang, -C is taken. We might want to reshuffle this. */
00414     {"icritical", required_argument, 0, 'K'},
00415     {"kilobytes", no_argument, 0, 'k'},
00416     {"megabytes", no_argument, 0, 'm'},
00417     {"units", required_argument, 0, 'u'},
00418     {"path", required_argument, 0, 'p'},
00419     {"partition", required_argument, 0, 'p'},
00420     {"exclude_device", required_argument, 0, 'x'},
00421     {"exclude-type", required_argument, 0, 'X'},
00422     {"group", required_argument, 0, 'g'},
00423     {"eregi-path", required_argument, 0, 'R'},
00424     {"eregi-partition", required_argument, 0, 'R'},
00425     {"ereg-path", required_argument, 0, 'r'},
00426     {"ereg-partition", required_argument, 0, 'r'},
00427     {"ignore-ereg-path", required_argument, 0, 'i'},
00428     {"ignore-ereg-partition", required_argument, 0, 'i'},
00429     {"ignore-eregi-path", required_argument, 0, 'I'},
00430     {"ignore-eregi-partition", required_argument, 0, 'I'},
00431     {"local", no_argument, 0, 'l'},
00432     {"stat-remote-fs", no_argument, 0, 'L'},
00433     {"mountpoint", no_argument, 0, 'M'},
00434     {"errors-only", no_argument, 0, 'e'},
00435     {"exact-match", no_argument, 0, 'E'},
00436     {"all", no_argument, 0, 'A'},
00437     {"verbose", no_argument, 0, 'v'},
00438     {"quiet", no_argument, 0, 'q'},
00439     {"clear", no_argument, 0, 'C'},
00440     {"version", no_argument, 0, 'V'},
00441     {"help", no_argument, 0, 'h'},
00442     {0, 0, 0, 0}
00443   };
00444 
00445   if (argc < 2)
00446     return ERROR;
00447 
00448   np_add_name(&fs_exclude_list, "iso9660");
00449 
00450   for (c = 1; c < argc; c++)
00451     if (strcmp ("-to", argv[c]) == 0)
00452       strcpy (argv[c], "-t");
00453 
00454   while (1) {
00455     c = getopt_long (argc, argv, "+?VqhveCt:c:w:K:W:u:p:x:X:mklLg:R:r:i:I:MEA", longopts, &option);
00456 
00457     if (c == -1 || c == EOF)
00458       break;
00459 
00460     switch (c) {
00461     case 't':                 /* timeout period */
00462       if (is_integer (optarg)) {
00463         timeout_interval = atoi (optarg);
00464         break;
00465       }
00466       else {
00467         usage2 (_("Timeout interval must be a positive integer"), optarg);
00468       }
00469 
00470     /* See comments for 'c' */
00471     case 'w':                 /* warning threshold */
00472       if (strstr(optarg, "%")) {
00473         if (*optarg == '@') {
00474           warn_freespace_percent = optarg;
00475         } else {
00476           asprintf(&warn_freespace_percent, "@%s", optarg);
00477         }
00478       } else {
00479         if (*optarg == '@') {
00480           warn_freespace_units = optarg;
00481         } else {
00482           asprintf(&warn_freespace_units, "@%s", optarg);
00483         }
00484       }
00485       break;
00486 
00487     /* Awful mistake where the range values do not make sense. Normally,
00488        you alert if the value is within the range, but since we are using
00489        freespace, we have to alert if outside the range. Thus we artifically
00490        force @ at the beginning of the range, so that it is backwards compatible
00491     */
00492     case 'c':                 /* critical threshold */
00493       if (strstr(optarg, "%")) {
00494         if (*optarg == '@') {
00495           crit_freespace_percent = optarg;
00496         } else {
00497           asprintf(&crit_freespace_percent, "@%s", optarg);
00498         }
00499       } else {
00500         if (*optarg == '@') {
00501           crit_freespace_units = optarg;
00502         } else {
00503           asprintf(&crit_freespace_units, "@%s", optarg);
00504         }
00505       }
00506       break;
00507 
00508     case 'W':               /* warning inode threshold */
00509       if (*optarg == '@') {
00510         warn_freeinodes_percent = optarg;
00511       } else {
00512         asprintf(&warn_freeinodes_percent, "@%s", optarg);
00513       }
00514       break;
00515     case 'K':               /* critical inode threshold */
00516       if (*optarg == '@') {
00517         crit_freeinodes_percent = optarg;
00518       } else {
00519         asprintf(&crit_freeinodes_percent, "@%s", optarg);
00520       }
00521       break;
00522     case 'u':
00523       if (units)
00524         free(units);
00525       if (! strcmp (optarg, "bytes")) {
00526         mult = (uintmax_t)1;
00527         units = strdup ("B");
00528       } else if (! strcmp (optarg, "kB")) {
00529         mult = (uintmax_t)1024;
00530         units = strdup ("kB");
00531       } else if (! strcmp (optarg, "MB")) {
00532         mult = (uintmax_t)1024 * 1024;
00533         units = strdup ("MB");
00534       } else if (! strcmp (optarg, "GB")) {
00535         mult = (uintmax_t)1024 * 1024 * 1024;
00536         units = strdup ("GB");
00537       } else if (! strcmp (optarg, "TB")) {
00538         mult = (uintmax_t)1024 * 1024 * 1024 * 1024;
00539         units = strdup ("TB");
00540       } else {
00541         die (STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
00542       }
00543       if (units == NULL)
00544         die (STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
00545       break;
00546     case 'k': /* display mountpoint */
00547       mult = 1024;
00548       if (units)
00549         free(units);
00550       units = strdup ("kB");
00551       break;
00552     case 'm': /* display mountpoint */
00553       mult = 1024 * 1024;
00554       if (units)
00555         free(units);
00556       units = strdup ("MB");
00557       break;
00558     case 'L':
00559       stat_remote_fs = 1;
00560     case 'l':
00561       show_local_fs = 1;
00562       break;
00563     case 'p':                 /* select path */
00564       if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
00565              crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
00566              warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
00567              crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
00568         die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n"));
00569       }
00570 
00571       /* add parameter if not found. overwrite thresholds if path has already been added  */
00572       if (! (se = np_find_parameter(path_select_list, optarg))) {
00573           se = np_add_parameter(&path_select_list, optarg);
00574       }
00575       se->group = group;
00576       set_all_thresholds(se);
00577 
00578       /* With autofs, it is required to stat() the path before re-populating the mount_list */
00579       stat_path(se);
00580       /* NB: We can't free the old mount_list "just like that": both list pointers and struct
00581        * pointers are copied around. One of the reason it wasn't done yet is that other parts
00582        * of check_disk need the same kind of cleanup so it'd better be done as a whole */
00583       mount_list = read_file_system_list (0);
00584       np_set_best_match(se, mount_list, exact_match);
00585 
00586       path_selected = TRUE;
00587       break;
00588     case 'x':                 /* exclude path or partition */
00589       np_add_name(&dp_exclude_list, optarg);
00590       break;
00591     case 'X':                 /* exclude file system type */
00592       np_add_name(&fs_exclude_list, optarg);
00593       break;
00594     case 'v':                 /* verbose */
00595       verbose++;
00596       break;
00597     case 'q':                 /* TODO: this function should eventually go away (removed 2007-09-20) */
00598       /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
00599       erronly = TRUE;
00600       break;
00601     case 'e':
00602       erronly = TRUE;
00603       break;
00604     case 'E':
00605       if (path_selected)
00606         die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n"));
00607       exact_match = TRUE;
00608       break;
00609     case 'g':
00610       if (path_selected)
00611         die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n"));
00612       group = optarg;
00613       break;
00614     case 'I':
00615       cflags |= REG_ICASE;
00616     case 'i':
00617       if (!path_selected)
00618         die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly"));
00619       err = regcomp(&re, optarg, cflags);
00620       if (err != 0) {
00621         regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
00622         die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
00623       }
00624 
00625       temp_list = path_select_list;
00626 
00627       previous = NULL;
00628       while (temp_list) {
00629         if (temp_list->best_match) {
00630           if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
00631 
00632               if (verbose >=3)
00633                 printf("ignoring %s matching regex\n", temp_list->name);
00634 
00635               temp_list = np_del_parameter(temp_list, previous);
00636               /* pointer to first element needs to be updated if first item gets deleted */
00637               if (previous == NULL)
00638                 path_select_list = temp_list;
00639           } else {
00640             previous = temp_list;
00641             temp_list = temp_list->name_next;
00642           }
00643         } else {
00644           previous = temp_list;
00645           temp_list = temp_list->name_next;
00646         }
00647       }
00648 
00649 
00650       cflags = default_cflags;
00651       break;
00652 
00653     case 'A':
00654       optarg = strdup(".*");
00655     case 'R':
00656       cflags |= REG_ICASE;
00657     case 'r':
00658       if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
00659              crit_freespace_percent || warn_usedspace_units || crit_usedspace_units ||
00660              warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent ||
00661              crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) {
00662         die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n"));
00663       }
00664 
00665       err = regcomp(&re, optarg, cflags);
00666       if (err != 0) {
00667         regerror (err, &re, errbuf, MAX_INPUT_BUFFER);
00668         die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf);
00669       }
00670 
00671       for (me = mount_list; me; me = me->me_next) {
00672         if (np_regex_match_mount_entry(me, &re)) {
00673           fnd = TRUE;
00674           if (verbose >= 3)
00675             printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg);
00676 
00677           /* add parameter if not found. overwrite thresholds if path has already been added  */
00678           if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) {
00679             se = np_add_parameter(&path_select_list, me->me_mountdir);
00680           }
00681           se->group = group;
00682           set_all_thresholds(se);
00683         }
00684       }
00685 
00686       if (!fnd)
00687         die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"),
00688             _("Regular expression did not match any path or disk"), optarg);
00689 
00690       fnd = FALSE;
00691       path_selected = TRUE;
00692       np_set_best_match(path_select_list, mount_list, exact_match);
00693       cflags = default_cflags;
00694 
00695       break;
00696     case 'M': /* display mountpoint */
00697       display_mntp = TRUE;
00698       break;
00699     case 'C':
00700        /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */
00701        if (path_selected == FALSE) {
00702          struct parameter_list *path;
00703          for (me = mount_list; me; me = me->me_next) {
00704            if (! (path = np_find_parameter(path_select_list, me->me_mountdir)))
00705              path = np_add_parameter(&path_select_list, me->me_mountdir);
00706            path->best_match = me;
00707            path->group = group;
00708            set_all_thresholds(path);
00709          }
00710       }
00711       warn_freespace_units = NULL;
00712       crit_freespace_units = NULL;
00713       warn_usedspace_units = NULL;
00714       crit_usedspace_units = NULL;
00715       warn_freespace_percent = NULL;
00716       crit_freespace_percent = NULL;
00717       warn_usedspace_percent = NULL;
00718       crit_usedspace_percent = NULL;
00719       warn_usedinodes_percent = NULL;
00720       crit_usedinodes_percent = NULL;
00721       warn_freeinodes_percent = NULL;
00722       crit_freeinodes_percent = NULL;
00723 
00724       path_selected = FALSE;
00725       group = NULL;
00726       break;
00727     case 'V':                 /* version */
00728       print_revision (progname, NP_VERSION);
00729       exit (STATE_OK);
00730     case 'h':                 /* help */
00731       print_help ();
00732       exit (STATE_OK);
00733     case '?':                 /* help */
00734       usage (_("Unknown argument"));
00735     }
00736   }
00737 
00738   /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
00739   c = optind;
00740   if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
00741     warn_usedspace_percent = argv[c++];
00742 
00743   if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c]))
00744     crit_usedspace_percent = argv[c++];
00745 
00746   if (argc > c && path == NULL) {
00747     se = np_add_parameter(&path_select_list, strdup(argv[c++]));
00748     path_selected = TRUE;
00749     set_all_thresholds(se);
00750   }
00751 
00752   if (units == NULL) {
00753     units = strdup ("MB");
00754     mult = (uintmax_t)1024 * 1024;
00755   }
00756 
00757   return TRUE;
00758 }
00759 
00760 
00761 
00762 void
00763 print_path (const char *mypath)
00764 {
00765   if (mypath == NULL)
00766     printf ("\n");
00767   else
00768     printf (_(" for %s\n"), mypath);
00769 }
00770 
00771 
00772 void
00773 set_all_thresholds (struct parameter_list *path)
00774 {
00775     if (path->freespace_units != NULL) free(path->freespace_units);
00776     set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units);
00777     if (path->freespace_percent != NULL) free (path->freespace_percent);
00778     set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent);
00779     if (path->usedspace_units != NULL) free (path->usedspace_units);
00780     set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units);
00781     if (path->usedspace_percent != NULL) free (path->usedspace_percent);
00782     set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent);
00783     if (path->usedinodes_percent != NULL) free (path->usedinodes_percent);
00784     set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent);
00785     if (path->freeinodes_percent != NULL) free (path->freeinodes_percent);
00786     set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent);
00787 }
00788 
00789 /* TODO: Remove?
00790 
00791 int
00792 validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath)
00793 {
00794   if (w < 0 && c < 0 && wp < 0.0 && cp < 0.0) {
00795     printf (_("INPUT ERROR: No thresholds specified"));
00796     print_path (mypath);
00797     return ERROR;
00798   }
00799   else if ((wp >= 0.0 || cp >= 0.0) &&
00800            (wp < 0.0 || cp < 0.0 || wp > 100.0 || cp > 100.0 || cp > wp)) {
00801     printf (_("\
00802 INPUT ERROR: C_DFP (%f) should be less than W_DFP (%.1f) and both should be between zero and 100 percent, inclusive"),
00803             cp, wp);
00804     print_path (mypath);
00805     return ERROR;
00806   }
00807   else if ((iwp >= 0.0 || icp >= 0.0) &&
00808            (iwp < 0.0 || icp < 0.0 || iwp > 100.0 || icp > 100.0 || icp > iwp)) {
00809     printf (_("\
00810 INPUT ERROR: C_IDFP (%f) should be less than W_IDFP (%.1f) and both should be between zero and 100 percent, inclusive"),
00811             icp, iwp);
00812     print_path (mypath);
00813     return ERROR;
00814   }
00815   else if ((w > 0 || c > 0) && (w == 0 || c == 0 || c > w)) {
00816     printf (_("\
00817 INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greater than zero"),
00818             (unsigned long)c, (unsigned long)w);
00819     print_path (mypath);
00820     return ERROR;
00821   }
00822 
00823   return OK;
00824 }
00825 
00826 */
00827 
00828 
00829 
00830 
00831 
00832 
00833 
00834 void
00835 print_help (void)
00836 {
00837   print_revision (progname, NP_VERSION);
00838 
00839   printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
00840   printf (COPYRIGHT, copyright, email);
00841 
00842   printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
00843   printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values"));
00844 
00845   printf ("\n\n");
00846 
00847   print_usage ();
00848 
00849   printf (UT_HELP_VRSN);
00850   printf (UT_EXTRA_OPTS);
00851 
00852   printf (" %s\n", "-w, --warning=INTEGER");
00853   printf ("    %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free"));
00854   printf (" %s\n", "-w, --warning=PERCENT%");
00855   printf ("    %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free"));
00856   printf (" %s\n", "-c, --critical=INTEGER");
00857   printf ("    %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free"));
00858   printf (" %s\n", "-c, --critical=PERCENT%");
00859   printf ("    %s\n", _("Exit with CRITCAL status if less than PERCENT of disk space is free"));
00860   printf (" %s\n", "-W, --iwarning=PERCENT%");
00861   printf ("    %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free"));
00862   printf (" %s\n", "-K, --icritical=PERCENT%");
00863   printf ("    %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
00864   printf (" %s\n", "-p, --path=PATH, --partition=PARTITION");
00865   printf ("    %s\n", _("Path or partition (may be repeated)"));
00866   printf (" %s\n", "-x, --exclude_device=PATH <STRING>");
00867   printf ("    %s\n", _("Ignore device (only works if -p unspecified)"));
00868   printf (" %s\n", "-C, --clear");
00869   printf ("    %s\n", _("Clear thresholds"));
00870   printf (" %s\n", "-E, --exact-match");
00871   printf ("    %s\n", _("For paths or partitions specified with -p, only check for exact paths"));
00872   printf (" %s\n", "-e, --errors-only");
00873   printf ("    %s\n", _("Display only devices/mountpoints with errors"));
00874   printf (" %s\n", "-g, --group=NAME");
00875   printf ("    %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together"));
00876   printf (" %s\n", "-k, --kilobytes");
00877   printf ("    %s\n", _("Same as '--units kB'"));
00878   printf (" %s\n", "-l, --local");
00879   printf ("    %s\n", _("Only check local filesystems"));
00880   printf (" %s\n", "-L, --stat-remote-fs");
00881   printf ("    %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
00882   printf ("    %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
00883   printf (" %s\n", "-M, --mountpoint");
00884   printf ("    %s\n", _("Display the mountpoint instead of the partition"));
00885   printf (" %s\n", "-m, --megabytes");
00886   printf ("    %s\n", _("Same as '--units MB'"));
00887   printf (" %s\n", "-A, --all");
00888   printf ("    %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
00889   printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
00890   printf ("    %s\n", _("Case insensitive regular expression for path/partition (may be repeated)"));
00891   printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
00892   printf ("    %s\n", _("Regular expression for path or partition (may be repeated)"));
00893   printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
00894   printf ("    %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)"));
00895   printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
00896   printf ("    %s\n", _("Regular expression to ignore selected path or partition (may be repeated)"));
00897   printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
00898   printf (" %s\n", "-u, --units=STRING");
00899   printf ("    %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)"));
00900   printf (UT_VERBOSE);
00901   printf (" %s\n", "-X, --exclude-type=TYPE");
00902   printf ("    %s\n", _("Ignore all filesystems of indicated type (may be repeated)"));
00903 
00904   printf ("\n");
00905   printf ("%s\n", _("Examples:"));
00906   printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
00907   printf ("    %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
00908   printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
00909   printf ("    %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
00910   printf ("    %s\n", _("are grouped which means the freespace thresholds are applied to all disks together"));
00911   printf (" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
00912   printf ("    %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
00913 
00914   printf (UT_SUPPORT);
00915 }
00916 
00917 
00918 
00919 void
00920 print_usage (void)
00921 {
00922   printf ("%s\n", _("Usage:"));
00923   printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname);
00924   printf ("[-C] [-E] [-e] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
00925   printf ("[-t timeout] [-u unit] [-v] [-X type]\n");
00926 }
00927 
00928 void
00929 stat_path (struct parameter_list *p)
00930 {
00931   /* Stat entry to check that dir exists and is accessible */
00932   if (verbose >= 3)
00933     printf("calling stat on %s\n", p->name);
00934   if (stat (p->name, &stat_buf[0])) {
00935     if (verbose >= 3)
00936       printf("stat failed on %s\n", p->name);
00937     printf("DISK %s - ", _("CRITICAL"));
00938     die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno));
00939   }
00940 }
00941 
00942 
00943 void
00944 get_stats (struct parameter_list *p, struct fs_usage *fsp) {
00945   struct parameter_list *p_list;
00946   struct fs_usage tmpfsp;
00947   int first = 1;
00948 
00949   if (p->group == NULL) {
00950     get_path_stats(p,fsp);
00951   } else {
00952     /* find all group members */
00953     for (p_list = path_select_list; p_list; p_list=p_list->name_next) {
00954       if (p_list->group && ! (strcmp(p_list->group, p->group))) {
00955         stat_path(p_list);
00956         get_fs_usage (p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
00957         get_path_stats(p_list, &tmpfsp); 
00958         if (verbose >= 3)
00959           printf("Group %s: adding %llu blocks sized %llu, (%s) used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
00960                  p_list->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, p_list->dfree_units,
00961                  p_list->dtotal_units, mult);
00962 
00963         /* prevent counting the first FS of a group twice since its parameter_list entry 
00964          * is used to carry the information of all file systems of the entire group */
00965         if (! first) {
00966           p->total += p_list->total;
00967           p->available += p_list->available;
00968           p->available_to_root += p_list->available_to_root;
00969           p->used += p_list->used;
00970             
00971           p->dused_units += p_list->dused_units;
00972           p->dfree_units += p_list->dfree_units;
00973           p->dtotal_units += p_list->dtotal_units;
00974           p->inodes_total += p_list->inodes_total;
00975           p->inodes_free  += p_list->inodes_free;
00976         }
00977         first = 0;
00978       }
00979       if (verbose >= 3) 
00980         printf("Group %s now has: used_units=%g free_units=%g total_units=%g fsu_blocksize=%llu mult=%llu\n",
00981                p->group, tmpfsp.fsu_bavail, tmpfsp.fsu_blocksize, p->best_match->me_mountdir, p->dused_units,
00982                p->dfree_units, p->dtotal_units, mult);
00983     }
00984     /* modify devname and mountdir for output */
00985     p->best_match->me_mountdir = p->best_match->me_devname = p->group;
00986   }
00987   /* finally calculate percentages for either plain FS or summed up group */
00988   p->dused_pct = calculate_percent( p->used, p->used + p->available );       /* used + available can never be > uintmax */
00989   p->dfree_pct = 100 - p->dused_pct;
00990   p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
00991   p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
00992   
00993 }
00994 
00995 void
00996 get_path_stats (struct parameter_list *p, struct fs_usage *fsp) {
00997   p->total = fsp->fsu_blocks;
00998   /* 2007-12-08 - Workaround for Gnulib reporting insanely high available
00999   * space on BSD (the actual value should be negative but fsp->fsu_bavail
01000   * is unsigned) */
01001   p->available = fsp->fsu_bavail > fsp->fsu_bfree ? 0 : fsp->fsu_bavail;
01002   p->available_to_root = fsp->fsu_bfree;
01003   p->used = p->total - p->available_to_root;
01004   
01005   p->dused_units = p->used*fsp->fsu_blocksize/mult;
01006   p->dfree_units = p->available*fsp->fsu_blocksize/mult;
01007   p->dtotal_units = p->total*fsp->fsu_blocksize/mult;
01008   p->inodes_total = fsp->fsu_files;      /* Total file nodes. */
01009   p->inodes_free  = fsp->fsu_ffree;      /* Free file nodes. */
01010   np_add_name(&seen, p->best_match->me_mountdir);
01011 }