X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=plugins%2Fcheck_disk.c;h=5c0eec592615e413f518e6b71bc3ccce39fa6d1e;hb=a179737771ece982e1840d480b10cb6ed1792619;hp=898e6cb4b505a5f8934c6375f6e2f2835f58c145;hpb=a8cd7705e7898d77764aa12ddcc2e29de1860138;p=nagiosplug.git diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 898e6cb..5c0eec5 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -1,50 +1,60 @@ -/****************************************************************************** - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - $Id$ - +/***************************************************************************** +* +* Nagios check_disk plugin +* +* License: GPL +* Copyright (c) 1999-2008 Nagios Plugins Development Team +* +* Description: +* +* This file contains the check_disk plugin +* +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* *****************************************************************************/ const char *progname = "check_disk"; const char *program_name = "check_disk"; /* Required for coreutils libs */ -const char *revision = "$Revision$"; -const char *copyright = "1999-2005"; +const char *copyright = "1999-2008"; const char *email = "nagiosplug-devel@lists.sourceforge.net"; - /* - * Additional inode code by Jorgen Lundman - */ - #include "common.h" +#ifdef HAVE_SYS_STAT_H +# include +#endif #if HAVE_INTTYPES_H # include #endif #include #include "popen.h" #include "utils.h" +#include "utils_disk.h" #include -#include "../lib/fsusage.h" -#include "../lib/mountlist.h" +#include "fsusage.h" +#include "mountlist.h" +#include "intprops.h" /* necessary for TYPE_MAXIMUM */ #if HAVE_LIMITS_H # include #endif +#include "regex.h" + /* If nonzero, show inode information. */ -static int inode_format; +static int inode_format = 1; /* If nonzero, show even filesystems with zero size or uninteresting types. */ @@ -53,6 +63,9 @@ static int show_all_fs = 1; /* If nonzero, show only local filesystems. */ static int show_local_fs = 0; +/* If nonzero, show only local filesystems but call stat() on remote ones. */ +static int stat_remote_fs = 0; + /* If positive, the units to use when printing sizes; if negative, the human-readable base. */ /* static int output_block_size; */ @@ -63,22 +76,6 @@ static int show_local_fs = 0; SunOs4.1.3, for one. It is *not* necessary on Linux. */ /* static int require_sync = 0; */ -/* A filesystem type to display. */ - -struct name_list -{ - char *name; - int found; - int found_len; - uintmax_t w_df; - uintmax_t c_df; - double w_dfp; - double c_dfp; - double w_idfp; - double c_idfp; - struct name_list *name_next; -}; - /* Linked list of filesystem types to display. If `fs_select_list' is NULL, list all types. This table is generated dynamically from command-line options, @@ -90,7 +87,7 @@ struct name_list Some filesystem types: 4.2 4.3 ufs nfs swap ignore io vm efs dbg */ -/* static struct name_list *fs_select_list; */ +/* static struct parameter_list *fs_select_list; */ /* Linked list of filesystem types to omit. If the list is empty, don't exclude any types. */ @@ -99,9 +96,7 @@ static struct name_list *fs_exclude_list; static struct name_list *dp_exclude_list; -static struct name_list *path_select_list; - -static struct name_list *dev_select_list; +static struct parameter_list *path_select_list = NULL; /* Linked list of mounted filesystems. */ static struct mount_entry *mount_list; @@ -121,18 +116,15 @@ enum int process_arguments (int, char **); void print_path (const char *mypath); +void set_all_thresholds (struct parameter_list *path); int validate_arguments (uintmax_t, uintmax_t, double, double, double, double, char *); -int check_disk (double usp, uintmax_t free_disk, double uisp); -int walk_name_list (struct name_list *list, const char *name); void print_help (void); void print_usage (void); +double calculate_percent(uintmax_t, uintmax_t); +void stat_path (struct parameter_list *p); -uintmax_t w_df = 0; -uintmax_t c_df = 0; double w_dfp = -1.0; double c_dfp = -1.0; -double w_idfp = -1.0; -double c_idfp = -1.0; char *path; char *exclude_device; char *units; @@ -140,32 +132,52 @@ uintmax_t mult = 1024 * 1024; int verbose = 0; int erronly = FALSE; int display_mntp = FALSE; - -/* Linked list of mounted filesystems. */ -static struct mount_entry *mount_list; - +int exact_match = FALSE; +char *warn_freespace_units = NULL; +char *crit_freespace_units = NULL; +char *warn_freespace_percent = NULL; +char *crit_freespace_percent = NULL; +char *warn_usedspace_units = NULL; +char *crit_usedspace_units = NULL; +char *warn_usedspace_percent = NULL; +char *crit_usedspace_percent = NULL; +char *warn_usedinodes_percent = NULL; +char *crit_usedinodes_percent = NULL; +char *warn_freeinodes_percent = NULL; +char *crit_freeinodes_percent = NULL; +int path_selected = FALSE; +char *group = NULL; +struct stat *stat_buf; int main (int argc, char **argv) { - double usp = -1.0, uisp = -1.0; int result = STATE_UNKNOWN; int disk_result = STATE_UNKNOWN; - char file_system[MAX_INPUT_BUFFER]; char *output; char *details; char *perf; - uintmax_t psize; - float free_space, free_space_pct, total_space, inode_space_pct; + char *preamble; + double inode_space_pct; + uintmax_t total, available, available_to_root, used; + double dfree_pct = -1, dused_pct = -1; + double dused_units, dfree_units, dtotal_units; + double dused_inodes_percent, dfree_inodes_percent; + double warning_high_tide; + double critical_high_tide; + int temp_result; struct mount_entry *me; - struct fs_usage fsp; - struct name_list *temp_list; + struct fs_usage fsp, tmpfsp; + struct parameter_list *temp_list, *path; + struct name_list *seen = NULL; - output = strdup (" - free space:"); + preamble = strdup (" - free space:"); + output = strdup (""); details = strdup (""); perf = strdup (""); + stat_buf = malloc(sizeof *stat_buf); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); @@ -173,141 +185,282 @@ main (int argc, char **argv) mount_list = read_file_system_list (0); + /* Parse extra opts if any */ + argv = np_extra_opts (&argc, argv, progname); + if (process_arguments (argc, argv) == ERROR) usage4 (_("Could not parse arguments")); - /* if a list of paths has been selected, preseed the list with - * the longest matching filesystem name by iterating across - * the mountlist once ahead of time. this will allow a query on - * "/var/log" to return information about "/var" if no "/var/log" - * filesystem exists, etc. this is the default behavior already - * with df-based checks, but for systems with their own space - * checking routines, this should make them more consistent. + /* If a list of paths has not been selected, find entire + mount list and create list of paths */ - if(path_select_list){ + if (path_selected == FALSE) { for (me = mount_list; me; me = me->me_next) { - walk_name_list(path_select_list, me->me_mountdir); - walk_name_list(path_select_list, me->me_devname); + if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) { + path = np_add_parameter(&path_select_list, me->me_mountdir); + } + path->best_match = me; + path->group = group; + set_all_thresholds(path); } - /* now pretend we never saw anything, but keep found_len. - * thus future searches will only match the best match */ - for (temp_list = path_select_list; temp_list; temp_list=temp_list->name_next){ - temp_list->found=0; + } + np_set_best_match(path_select_list, mount_list, exact_match); + + /* Error if no match found for specified paths */ + temp_list = path_select_list; + + while (temp_list) { + if (! temp_list->best_match) { + die (STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), temp_list->name); } + + temp_list = temp_list->name_next; } - /* for every mount entry */ - for (me = mount_list; me; me = me->me_next) { - /* if there's a list of paths to select, the current mount - * entry matches in path or device name, get fs usage */ - if (path_select_list && - (walk_name_list (path_select_list, me->me_mountdir) || - walk_name_list (path_select_list, me->me_devname) ) ) { - get_fs_usage (me->me_mountdir, me->me_devname, &fsp); - /* else if there's a list of paths/devices to select (but - * we didn't match above) skip to the next mount entry */ - } else if (dev_select_list || path_select_list) { - continue; - /* skip remote filesystems if we're not interested in them */ - } else if (me->me_remote && show_local_fs) { - continue; - /* skip pseudo fs's if we haven't asked for all fs's */ - } else if (me->me_dummy && !show_all_fs) { - continue; - /* skip excluded fstypes */ - } else if (fs_exclude_list && walk_name_list (fs_exclude_list, me->me_type)) { - continue; - /* skip excluded fs's */ - } else if (dp_exclude_list && - (walk_name_list (dp_exclude_list, me->me_devname) || - walk_name_list (dp_exclude_list, me->me_mountdir))) { + /* Process for every path in list */ + for (path = path_select_list; path; path=path->name_next) { + + if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) + printf("Thresholds(pct) for %s warn: %f crit %f\n",path->name, path->freespace_percent->warning->end, + path->freespace_percent->critical->end); + + if (verbose >= 3 && path->group != NULL) + printf("Group of %s: %s\n",path->name,path->group); + + /* reset disk result */ + disk_result = STATE_UNKNOWN; + + me = path->best_match; + + /* Filters */ + + /* Remove filesystems already seen */ + if (np_seen_name(seen, me->me_mountdir)) { continue; - /* otherwise, get fs usage */ } else { + if (path->group != NULL) { + /* find all group members */ + fsp.fsu_blocksize = 0; + fsp.fsu_blocks = 0; + fsp.fsu_bfree = 0; + fsp.fsu_bavail = 0; + fsp.fsu_files = 0; + fsp.fsu_ffree = 0; + + + for (temp_list = path_select_list; temp_list; temp_list=temp_list->name_next) { + if (temp_list->group && ! (strcmp(temp_list->group, path->group))) { + + stat_path(path); + get_fs_usage (temp_list->best_match->me_mountdir, temp_list->best_match->me_devname, &tmpfsp); + + /* possibly differing blocksizes if disks are grouped. Calculating average */ + fsp.fsu_blocksize = (fsp.fsu_blocksize * fsp.fsu_blocks + tmpfsp.fsu_blocksize * tmpfsp.fsu_blocks) / \ + (fsp.fsu_blocks + tmpfsp.fsu_blocks); /* Size of a block. */ + fsp.fsu_blocks += tmpfsp.fsu_blocks; /* Total blocks. */ + fsp.fsu_bfree += tmpfsp.fsu_bfree; /* Free blocks available to superuser. */ + /* Gnulib workaround - see comment about it a few lines below */ + fsp.fsu_bavail += (tmpfsp.fsu_bavail > tmpfsp.fsu_bfree ? 0 : tmpfsp.fsu_bavail); /* Free blocks available to non-superuser. */ + fsp.fsu_files += tmpfsp.fsu_files; /* Total file nodes. */ + fsp.fsu_ffree += tmpfsp.fsu_ffree; /* Free file nodes. */ + + if (verbose >= 3) + printf("Group %s: add %llu blocks (%s) \n", path->group, tmpfsp.fsu_bavail, temp_list->name); + /* printf("Group %s: add %u blocks (%s)\n", temp_list->name); *//* path->group, tmpfsp.fsu_bavail, temp_list->name); */ + + np_add_name(&seen, temp_list->best_match->me_mountdir); + } + } + /* modify devname and mountdir for output */ + me->me_mountdir = me->me_devname = path->group; + } else + np_add_name(&seen, me->me_mountdir); + } + + if (path->group == NULL) { + /* Skip remote filesystems if we're not interested in them */ + if (me->me_remote && show_local_fs) { + if (stat_remote_fs) + stat_path(path); + continue; + /* Skip pseudo fs's if we haven't asked for all fs's */ + } else if (me->me_dummy && !show_all_fs) { + continue; + /* Skip excluded fstypes */ + } else if (fs_exclude_list && np_find_name (fs_exclude_list, me->me_type)) { + continue; + /* Skip excluded fs's */ + } else if (dp_exclude_list && + (np_find_name (dp_exclude_list, me->me_devname) || + np_find_name (dp_exclude_list, me->me_mountdir))) { + continue; + } + + stat_path(path); get_fs_usage (me->me_mountdir, me->me_devname, &fsp); } if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) { - usp = (double)(fsp.fsu_blocks - fsp.fsu_bavail) * 100 / fsp.fsu_blocks; - uisp = (double)(fsp.fsu_files - fsp.fsu_ffree) * 100 / fsp.fsu_files; - disk_result = check_disk (usp, fsp.fsu_bavail, uisp); + total = fsp.fsu_blocks; + /* 2007-12-08 - Workaround for Gnulib reporting insanely high available + * space on BSD (the actual value should be negative but fsp.fsu_bavail + * is unsigned) */ + available = fsp.fsu_bavail > fsp.fsu_bfree ? 0 : fsp.fsu_bavail; + available_to_root = fsp.fsu_bfree; + used = total - available_to_root; + + if (verbose >= 3) + printf ("For %s, total=%llu, available=%llu, available_to_root=%llu, used=%llu, fsp.fsu_files=%llu, fsp.fsu_ffree=%llu\n", + me->me_mountdir, total, available, available_to_root, used, fsp.fsu_files, fsp.fsu_ffree); + + dused_pct = calculate_percent( used, used + available ); /* used + available can never be > uintmax */ + + dfree_pct = 100 - dused_pct; + dused_units = used*fsp.fsu_blocksize/mult; + dfree_units = available*fsp.fsu_blocksize/mult; + dtotal_units = total*fsp.fsu_blocksize/mult; + dused_inodes_percent = calculate_percent(fsp.fsu_files - fsp.fsu_ffree, fsp.fsu_files); + dfree_inodes_percent = 100 - dused_inodes_percent; + + if (verbose >= 3) { + 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", + me->me_mountdir, dused_pct, dfree_pct, dused_units, dfree_units, dtotal_units, dused_inodes_percent, dfree_inodes_percent, fsp.fsu_blocksize, mult); + } + /* Threshold comparisons */ - result = max_state (disk_result, result); - psize = fsp.fsu_blocks*fsp.fsu_blocksize/mult; + temp_result = get_status(dfree_units, path->freespace_units); + if (verbose >=3) printf("Freespace_units result=%d\n", temp_result); + disk_result = max_state( disk_result, temp_result ); + temp_result = get_status(dfree_pct, path->freespace_percent); + if (verbose >=3) printf("Freespace%% result=%d\n", temp_result); + disk_result = max_state( disk_result, temp_result ); - /* Moved this computation up here so we can add it - * to perf */ - inode_space_pct = (float)fsp.fsu_ffree*100/fsp.fsu_files; + temp_result = get_status(dused_units, path->usedspace_units); + if (verbose >=3) printf("Usedspace_units result=%d\n", temp_result); + disk_result = max_state( disk_result, temp_result ); + temp_result = get_status(dused_pct, path->usedspace_percent); + if (verbose >=3) printf("Usedspace_percent result=%d\n", temp_result); + disk_result = max_state( disk_result, temp_result ); + temp_result = get_status(dused_inodes_percent, path->usedinodes_percent); + if (verbose >=3) printf("Usedinodes_percent result=%d\n", temp_result); + disk_result = max_state( disk_result, temp_result ); + + temp_result = get_status(dfree_inodes_percent, path->freeinodes_percent); + if (verbose >=3) printf("Freeinodes_percent result=%d\n", temp_result); + disk_result = max_state( disk_result, temp_result ); + + result = max_state(result, disk_result); + + /* What a mess of units. The output shows free space, the perf data shows used space. Yikes! + Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf + data. Assumption that start=0. Roll on new syntax... + */ + + /* *_high_tide must be reinitialized at each run */ + warning_high_tide = UINT_MAX; + critical_high_tide = UINT_MAX; + + if (path->freespace_units->warning != NULL) { + warning_high_tide = dtotal_units - path->freespace_units->warning->end; + } + if (path->freespace_percent->warning != NULL) { + warning_high_tide = abs( min( (double) warning_high_tide, (double) (1.0 - path->freespace_percent->warning->end/100)*dtotal_units )); + } + if (path->freespace_units->critical != NULL) { + critical_high_tide = dtotal_units - path->freespace_units->critical->end; + } + if (path->freespace_percent->critical != NULL) { + critical_high_tide = abs( min( (double) critical_high_tide, (double) (1.0 - path->freespace_percent->critical->end/100)*dtotal_units )); + } + + /* Nb: *_high_tide are unset when == UINT_MAX */ asprintf (&perf, "%s %s", perf, - perfdata ((!strcmp(file_system, "none") || display_mntp) ? me->me_devname : me->me_mountdir, - psize-(fsp.fsu_bavail*fsp.fsu_blocksize/mult), units, - TRUE, min ((uintmax_t)psize-(uintmax_t)w_df, (uintmax_t)((1.0-w_dfp/100.0)*psize)), - TRUE, min ((uintmax_t)psize-(uintmax_t)c_df, (uintmax_t)((1.0-c_dfp/100.0)*psize)), - TRUE, inode_space_pct, + perfdata ((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, + dused_units, units, + (warning_high_tide != UINT_MAX ? TRUE : FALSE), warning_high_tide, + (critical_high_tide != UINT_MAX ? TRUE : FALSE), critical_high_tide, + TRUE, 0, + TRUE, dtotal_units)); - TRUE, psize)); if (disk_result==STATE_OK && erronly && !verbose) continue; - free_space = (float)fsp.fsu_bavail*fsp.fsu_blocksize/mult; - free_space_pct = (float)fsp.fsu_bavail*100/fsp.fsu_blocks; - total_space = (float)fsp.fsu_blocks*fsp.fsu_blocksize/mult; - if (disk_result!=STATE_OK || verbose>=0) - asprintf (&output, ("%s %s %.0f %s (%.0f%% inode=%.0f%%);"), - output, - (!strcmp(file_system, "none") || display_mntp) ? me->me_devname : me->me_mountdir, - free_space, - units, - free_space_pct, - inode_space_pct); + asprintf (&output, "%s %s %.0f %s (%.0f%%", + output, + (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, + dfree_units, + units, + dfree_pct); + if (dused_inodes_percent < 0) { + asprintf(&output, "%s inode=-);", output); + } else { + asprintf(&output, "%s inode=%.0f%%);", output, dfree_inodes_percent ); + } + /* TODO: Need to do a similar debug line asprintf (&details, _("%s\n\ %.0f of %.0f %s (%.0f%% inode=%.0f%%) free on %s (type %s mounted on %s) warn:%lu crit:%lu warn%%:%.0f%% crit%%:%.0f%%"), - details, free_space, total_space, units, free_space_pct, inode_space_pct, + details, dfree_units, dtotal_units, units, dfree_pct, inode_space_pct, me->me_devname, me->me_type, me->me_mountdir, (unsigned long)w_df, (unsigned long)c_df, w_dfp, c_dfp); + */ } } - if (verbose > 2) + if (verbose >= 2) asprintf (&output, "%s%s", output, details); - /* Override result if paths specified and not found */ - temp_list = path_select_list; - while (temp_list) { - if (!temp_list->found) { - asprintf (&output, _("%s [%s not found]"), output, temp_list->name); - result = STATE_CRITICAL; - } - temp_list = temp_list->name_next; - } - printf ("DISK %s%s|%s\n", state_text (result), output, perf); + printf ("DISK %s%s%s|%s\n", state_text (result), (erronly && result==STATE_OK) ? "" : preamble, output, perf); return result; } +double calculate_percent(uintmax_t value, uintmax_t total) { + double pct = -1; + /* I don't understand the below, but it is taken from coreutils' df */ + /* Seems to be calculating pct, in the best possible way */ + if (value <= TYPE_MAXIMUM(uintmax_t) / 100 + && total != 0) { + uintmax_t u100 = value * 100; + pct = u100 / total + (u100 % total != 0); + } else { + /* Possible rounding errors - see coreutils' df for more explanation */ + double u = value; + double t = total; + if (t) { + long int lipct = pct = u * 100 / t; + double ipct = lipct; + + /* Like 'pct = ceil (dpct);', but without ceil - from coreutils again */ + if (ipct - 1 < pct && pct <= ipct + 1) + pct = ipct + (ipct < pct); + } + } + return pct; +} /* process command-line arguments */ int process_arguments (int argc, char **argv) { - int c; - struct name_list *se; - struct name_list **pathtail = &path_select_list; - struct name_list **fstail = &fs_exclude_list; - struct name_list **dptail = &dp_exclude_list; - struct name_list *temp_list; + int c, err; + struct parameter_list *se; + struct parameter_list *temp_list = NULL, *previous = NULL; + struct parameter_list *temp_path_select_list = NULL; + struct mount_entry *me, *temp_me; int result = OK; - struct stat *stat_buf; - - unsigned long l; + regex_t re; + int cflags = REG_NOSUB | REG_EXTENDED; + int default_cflags = cflags; + char errbuf[MAX_INPUT_BUFFER]; + int fnd = 0; int option = 0; static struct option longopts[] = { @@ -317,16 +470,28 @@ process_arguments (int argc, char **argv) {"iwarning", required_argument, 0, 'W'}, /* Dang, -C is taken. We might want to reshuffle this. */ {"icritical", required_argument, 0, 'K'}, - {"local", required_argument, 0, 'l'}, - {"kilobytes", required_argument, 0, 'k'}, - {"megabytes", required_argument, 0, 'm'}, + {"kilobytes", no_argument, 0, 'k'}, + {"megabytes", no_argument, 0, 'm'}, {"units", required_argument, 0, 'u'}, {"path", required_argument, 0, 'p'}, {"partition", required_argument, 0, 'p'}, {"exclude_device", required_argument, 0, 'x'}, {"exclude-type", required_argument, 0, 'X'}, + {"group", required_argument, 0, 'g'}, + {"eregi-path", required_argument, 0, 'R'}, + {"eregi-partition", required_argument, 0, 'R'}, + {"ereg-path", required_argument, 0, 'r'}, + {"ereg-partition", required_argument, 0, 'r'}, + {"ignore-ereg-path", required_argument, 0, 'i'}, + {"ignore-ereg-partition", required_argument, 0, 'i'}, + {"ignore-eregi-path", required_argument, 0, 'I'}, + {"ignore-eregi-partition", required_argument, 0, 'I'}, + {"local", no_argument, 0, 'l'}, + {"stat-remote-fs", no_argument, 0, 'L'}, {"mountpoint", no_argument, 0, 'M'}, {"errors-only", no_argument, 0, 'e'}, + {"exact-match", no_argument, 0, 'E'}, + {"all", no_argument, 0, 'A'}, {"verbose", no_argument, 0, 'v'}, {"quiet", no_argument, 0, 'q'}, {"clear", no_argument, 0, 'C'}, @@ -338,20 +503,14 @@ process_arguments (int argc, char **argv) if (argc < 2) return ERROR; - se = (struct name_list *) malloc (sizeof (struct name_list)); - se->name = strdup ("iso9660"); - se->name_next = NULL; - se->found = 0; - se->found_len = 0; - *fstail = se; - fstail = &se->name_next; + np_add_name(&fs_exclude_list, "iso9660"); for (c = 1; c < argc; c++) if (strcmp ("-to", argv[c]) == 0) strcpy (argv[c], "-t"); while (1) { - c = getopt_long (argc, argv, "+?VqhveCt:c:w:K:W:u:p:x:X:mklM", longopts, &option); + c = getopt_long (argc, argv, "+?VqhveCt:c:w:K:W:u:p:x:X:mklLg:R:r:i:I:MEA", longopts, &option); if (c == -1 || c == EOF) break; @@ -365,56 +524,59 @@ process_arguments (int argc, char **argv) else { usage2 (_("Timeout interval must be a positive integer"), optarg); } + + /* See comments for 'c' */ case 'w': /* warning threshold */ - if (is_intnonneg (optarg)) { - w_df = atoi (optarg); - break; - } - else if (strpbrk (optarg, ",:") && - strstr (optarg, "%") && - sscanf (optarg, "%lu%*[:,]%lf%%", &l, &w_dfp) == 2) { - w_df = (uintmax_t)l; - break; - } - else if (strstr (optarg, "%") && sscanf (optarg, "%lf%%", &w_dfp) == 1) { - break; - } - else { - usage4 (_("Warning threshold must be integer or percentage!")); + if (strstr(optarg, "%")) { + if (*optarg == '@') { + warn_freespace_percent = optarg; + } else { + asprintf(&warn_freespace_percent, "@%s", optarg); + } + } else { + if (*optarg == '@') { + warn_freespace_units = optarg; + } else { + asprintf(&warn_freespace_units, "@%s", optarg); + } } + break; + + /* Awful mistake where the range values do not make sense. Normally, + you alert if the value is within the range, but since we are using + freespace, we have to alert if outside the range. Thus we artifically + force @ at the beginning of the range, so that it is backwards compatible + */ case 'c': /* critical threshold */ - if (is_intnonneg (optarg)) { - c_df = atoi (optarg); - break; - } - else if (strpbrk (optarg, ",:") && - strstr (optarg, "%") && - sscanf (optarg, "%lu%*[,:]%lf%%", &l, &c_dfp) == 2) { - c_df = (uintmax_t)l; - break; + if (strstr(optarg, "%")) { + if (*optarg == '@') { + crit_freespace_percent = optarg; + } else { + asprintf(&crit_freespace_percent, "@%s", optarg); + } + } else { + if (*optarg == '@') { + crit_freespace_units = optarg; + } else { + asprintf(&crit_freespace_units, "@%s", optarg); + } } - else if (strstr (optarg, "%") && sscanf (optarg, "%lf%%", &c_dfp) == 1) { - break; + break; + + case 'W': /* warning inode threshold */ + if (*optarg == '@') { + warn_freeinodes_percent = optarg; + } else { + asprintf(&warn_freeinodes_percent, "@%s", optarg); } - else { - usage4 (_("Critical threshold must be integer or percentage!")); + break; + case 'K': /* critical inode threshold */ + if (*optarg == '@') { + crit_freeinodes_percent = optarg; + } else { + asprintf(&crit_freeinodes_percent, "@%s", optarg); } - - - case 'W': /* warning inode threshold */ - if (strstr (optarg, "%") && sscanf (optarg, "%lf%%", &w_idfp) == 1) { - break; - } - else { - usage (_("Warning inode threshold must be percentage!\n")); - } - case 'K': /* kritical inode threshold */ - if (strstr (optarg, "%") && sscanf (optarg, "%lf%%", &c_idfp) == 1) { - break; - } - else { - usage (_("Critical inode threshold must be percentage!\n")); - } + break; case 'u': if (units) free(units); @@ -451,85 +613,177 @@ process_arguments (int argc, char **argv) free(units); units = strdup ("MB"); break; + case 'L': + stat_remote_fs = 1; case 'l': - show_local_fs = 1; + show_local_fs = 1; break; case 'p': /* select path */ - se = (struct name_list *) malloc (sizeof (struct name_list)); - se->name = optarg; - se->name_next = NULL; - se->w_df = w_df; - se->c_df = c_df; - se->w_dfp = w_dfp; - se->c_dfp = c_dfp; - se->w_idfp = w_idfp; - se->c_idfp = c_idfp; - se->found = 0; - se->found_len = 0; - *pathtail = se; - pathtail = &se->name_next; + if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent || + crit_freespace_percent || warn_usedspace_units || crit_usedspace_units || + warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent || + crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) { + die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); + } + + /* add parameter if not found. overwrite thresholds if path has already been added */ + if (! (se = np_find_parameter(path_select_list, optarg))) { + se = np_add_parameter(&path_select_list, optarg); + } + se->group = group; + set_all_thresholds(se); + + /* With autofs, it is required to stat() the path before re-populating the mount_list */ + stat_path(se); + /* NB: We can't free the old mount_list "just like that": both list pointers and struct + * pointers are copied around. One of the reason it wasn't done yet is that other parts + * of check_disk need the same kind of cleanup so it'd better be done as a whole */ + mount_list = read_file_system_list (0); + np_set_best_match(se, mount_list, exact_match); + + path_selected = TRUE; break; case 'x': /* exclude path or partition */ - se = (struct name_list *) malloc (sizeof (struct name_list)); - se->name = optarg; - se->name_next = NULL; - - /* If you don't clear the w_fd etc values here, they - * get processed when you walk the list and assigned - * to the global w_df! - */ - se->w_df = 0; - se->c_df = 0; - se->w_dfp = 0; - se->c_dfp = 0; - se->w_idfp = 0; - se->c_idfp = 0; - se->found = 0; - se->found_len = 0; - *dptail = se; - dptail = &se->name_next; + np_add_name(&dp_exclude_list, optarg); break; case 'X': /* exclude file system type */ - se = (struct name_list *) malloc (sizeof (struct name_list)); - se->name = optarg; - se->name_next = NULL; - /* If you don't clear the w_fd etc values here, they - * get processed when you walk the list and assigned - * to the global w_df! - */ - se->w_df = 0; - se->c_df = 0; - se->w_dfp = 0; - se->c_dfp = 0; - se->w_idfp = 0; - se->c_idfp = 0; - se->found = 0; - se->found_len = 0; - *fstail = se; - fstail = &se->name_next; + np_add_name(&fs_exclude_list, optarg); break; case 'v': /* verbose */ verbose++; break; - case 'q': /* verbose */ - verbose--; + case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ + /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ + erronly = TRUE; break; case 'e': erronly = TRUE; + break; + case 'E': + if (path_selected) + die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); + exact_match = TRUE; + break; + case 'g': + if (path_selected) + die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); + group = optarg; + break; + case 'I': + cflags |= REG_ICASE; + case 'i': + if (!path_selected) + die (STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); + err = regcomp(&re, optarg, cflags); + if (err != 0) { + regerror (err, &re, errbuf, MAX_INPUT_BUFFER); + die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf); + } + + temp_list = path_select_list; + + previous = NULL; + while (temp_list) { + if (temp_list->best_match) { + if (np_regex_match_mount_entry(temp_list->best_match, &re)) { + + if (verbose >=3) + printf("ignoring %s matching regex\n", temp_list->name); + + temp_list = np_del_parameter(temp_list, previous); + /* pointer to first element needs to be updated if first item gets deleted */ + if (previous == NULL) + path_select_list = temp_list; + } else { + previous = temp_list; + temp_list = temp_list->name_next; + } + } else { + previous = temp_list; + temp_list = temp_list->name_next; + } + } + + + cflags = default_cflags; + break; + + case 'A': + optarg = strdup(".*"); + case 'R': + cflags |= REG_ICASE; + case 'r': + if (! (warn_freespace_units || crit_freespace_units || warn_freespace_percent || + crit_freespace_percent || warn_usedspace_units || crit_usedspace_units || + warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent || + crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent )) { + die (STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R\n")); + } + + err = regcomp(&re, optarg, cflags); + if (err != 0) { + regerror (err, &re, errbuf, MAX_INPUT_BUFFER); + die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), _("Could not compile regular expression"), errbuf); + } + + for (me = mount_list; me; me = me->me_next) { + if (np_regex_match_mount_entry(me, &re)) { + fnd = TRUE; + if (verbose >= 3) + printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); + + /* add parameter if not found. overwrite thresholds if path has already been added */ + if (! (se = np_find_parameter(path_select_list, me->me_mountdir))) { + se = np_add_parameter(&path_select_list, me->me_mountdir); + } + se->group = group; + set_all_thresholds(se); + } + } + + if (!fnd) + die (STATE_UNKNOWN, "DISK %s: %s - %s\n",_("UNKNOWN"), + _("Regular expression did not match any path or disk"), optarg); + + fnd = FALSE; + path_selected = TRUE; + np_set_best_match(path_select_list, mount_list, exact_match); + cflags = default_cflags; + break; case 'M': /* display mountpoint */ display_mntp = TRUE; break; case 'C': - w_df = 0; - c_df = 0; - w_dfp = -1.0; - c_dfp = -1.0; - w_idfp = -1.0; - c_idfp = -1.0; + /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ + if (path_selected == FALSE) { + struct parameter_list *path; + for (me = mount_list; me; me = me->me_next) { + if (! (path = np_find_parameter(path_select_list, me->me_mountdir))) + path = np_add_parameter(&path_select_list, me->me_mountdir); + path->best_match = me; + path->group = group; + set_all_thresholds(path); + } + } + warn_freespace_units = NULL; + crit_freespace_units = NULL; + warn_usedspace_units = NULL; + crit_usedspace_units = NULL; + warn_freespace_percent = NULL; + crit_freespace_percent = NULL; + warn_usedspace_percent = NULL; + crit_usedspace_percent = NULL; + warn_usedinodes_percent = NULL; + crit_usedinodes_percent = NULL; + warn_freeinodes_percent = NULL; + crit_freeinodes_percent = NULL; + + path_selected = FALSE; + group = NULL; break; case 'V': /* version */ - print_revision (progname, revision); + print_revision (progname, NP_VERSION); exit (STATE_OK); case 'h': /* help */ print_help (); @@ -539,69 +793,58 @@ process_arguments (int argc, char **argv) } } - /* Support for "check_disk warn crit [fs]" with thresholds at used level */ + /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ c = optind; - if (w_dfp < 0 && argc > c && is_intnonneg (argv[c])) - w_dfp = (100.0 - atof (argv[c++])); + if (warn_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c])) + warn_usedspace_percent = argv[c++]; - if (c_dfp < 0 && argc > c && is_intnonneg (argv[c])) - c_dfp = (100.0 - atof (argv[c++])); + if (crit_usedspace_percent == NULL && argc > c && is_intnonneg (argv[c])) + crit_usedspace_percent = argv[c++]; if (argc > c && path == NULL) { - se = (struct name_list *) malloc (sizeof (struct name_list)); - se->name = strdup (argv[c++]); - se->name_next = NULL; - se->w_df = w_df; - se->c_df = c_df; - se->w_dfp = w_dfp; - se->c_dfp = c_dfp; - se->w_idfp = w_idfp; - se->c_idfp = c_idfp; - se->found =0; - se->found_len = 0; - *pathtail = se; + se = np_add_parameter(&path_select_list, strdup(argv[c++])); + path_selected = TRUE; + set_all_thresholds(se); } - if (path_select_list) { - temp_list = path_select_list; - stat_buf = malloc(sizeof *stat_buf); - while (temp_list) { - /* Stat each entry to check that dir exists */ - if (stat (temp_list->name, &stat_buf[0])) { - printf("DISK %s - ", _("CRITICAL")); - die (STATE_CRITICAL, _("%s does not exist\n"), temp_list->name); - } - if (validate_arguments (temp_list->w_df, - temp_list->c_df, - temp_list->w_dfp, - temp_list->c_dfp, - temp_list->w_idfp, - temp_list->c_idfp, - temp_list->name) == ERROR) - result = ERROR; - temp_list = temp_list->name_next; - } - free(stat_buf); - return result; - } else { - return validate_arguments (w_df, c_df, w_dfp, c_dfp, w_idfp, c_idfp, NULL); + if (units == NULL) { + units = strdup ("MB"); + mult = (uintmax_t)1024 * 1024; } + + return TRUE; } void -print_path (const char *mypath) +print_path (const char *mypath) { if (mypath == NULL) printf ("\n"); else printf (_(" for %s\n"), mypath); - - return; } +void +set_all_thresholds (struct parameter_list *path) +{ + if (path->freespace_units != NULL) free(path->freespace_units); + set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); + if (path->freespace_percent != NULL) free (path->freespace_percent); + set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); + if (path->usedspace_units != NULL) free (path->usedspace_units); + set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); + if (path->usedspace_percent != NULL) free (path->usedspace_percent); + set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); + if (path->usedinodes_percent != NULL) free (path->usedinodes_percent); + set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); + if (path->freeinodes_percent != NULL) free (path->freeinodes_percent); + set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); +} + +/* TODO: Remove? int validate_arguments (uintmax_t w, uintmax_t c, double wp, double cp, double iwp, double icp, char *mypath) @@ -634,123 +877,104 @@ INPUT ERROR: C_DF (%lu) should be less than W_DF (%lu) and both should be greate print_path (mypath); return ERROR; } - - if (units == NULL) { - units = strdup ("MB"); - mult = (uintmax_t)1024 * 1024; - } + return OK; } +*/ -int - -check_disk (double usp, uintmax_t free_disk, double uisp) -{ - int result = STATE_UNKNOWN; - /* check the percent used space against thresholds */ - if (usp >= 0.0 && c_dfp >=0.0 && usp >= (100.0 - c_dfp)) - result = STATE_CRITICAL; - else if (uisp >= 0.0 && c_idfp >=0.0 && uisp >= (100.0 - c_idfp)) - result = STATE_CRITICAL; - else if (c_df > 0 && free_disk <= c_df) - result = STATE_CRITICAL; - else if (usp >= 0.0 && w_dfp >=0.0 && usp >= (100.0 - w_dfp)) - result = STATE_WARNING; - else if (uisp >= 0.0 && w_idfp >=0.0 && uisp >= (100.0 - w_idfp)) - result = STATE_WARNING; - else if (w_df > 0 && free_disk <= w_df) - result = STATE_WARNING; - else if (usp >= 0.0) - result = STATE_OK; - return result; -} - -int -walk_name_list (struct name_list *list, const char *name) -{ - int name_len; - name_len = strlen(name); - while (list) { - /* if the paths match up to the length of the mount path, - * AND if the mount path name is longer than the longest - * found match, we have a new winner */ - if (name_len >= list->found_len && - ! strncmp(list->name, name, name_len)) { - list->found = 1; - list->found_len = name_len; - /* if required for name_lists that have not saved w_df, etc (eg exclude lists) */ - if (list->w_df) w_df = list->w_df; - if (list->c_df) c_df = list->c_df; - if (list->w_dfp>=0.0) w_dfp = list->w_dfp; - if (list->c_dfp>=0.0) c_dfp = list->c_dfp; - return TRUE; - } - list = list->name_next; - } - return FALSE; -} void print_help (void) { - print_revision (progname, revision); + print_revision (progname, NP_VERSION); printf ("Copyright (c) 1999 Ethan Galstad \n"); printf (COPYRIGHT, copyright, email); - printf (_("This plugin checks the amount of used disk space on a mounted file system")); - printf (_("and generates an alert if free space is less than one of the threshold values")); + printf ("%s\n", _("This plugin checks the amount of used disk space on a mounted file system")); + printf ("%s\n", _("and generates an alert if free space is less than one of the threshold values")); printf ("\n\n"); print_usage (); printf (_(UT_HELP_VRSN)); + printf (_(UT_EXTRA_OPTS)); printf (" %s\n", "-w, --warning=INTEGER"); printf (" %s\n", _("Exit with WARNING status if less than INTEGER units of disk are free")); printf (" %s\n", "-w, --warning=PERCENT%"); printf (" %s\n", _("Exit with WARNING status if less than PERCENT of disk space is free")); - printf (" %s\n", "-W, --iwarning=PERCENT%"); - printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free")); - printf (" %s\n", "-K, --icritical=PERCENT%"); - printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); printf (" %s\n", "-c, --critical=INTEGER"); printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER units of disk are free")); printf (" %s\n", "-c, --critical=PERCENT%"); printf (" %s\n", _("Exit with CRITCAL status if less than PERCENT of disk space is free")); + printf (" %s\n", "-W, --iwarning=PERCENT%"); + printf (" %s\n", _("Exit with WARNING status if less than PERCENT of inode space is free")); + printf (" %s\n", "-K, --icritical=PERCENT%"); + printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); + printf (" %s\n", "-p, --path=PATH, --partition=PARTITION"); + printf (" %s\n", _("Path or partition (may be repeated)")); + printf (" %s\n", "-x, --exclude_device=PATH "); + printf (" %s\n", _("Ignore device (only works if -p unspecified)")); printf (" %s\n", "-C, --clear"); printf (" %s\n", _("Clear thresholds")); - printf (" %s\n", "-u, --units=STRING"); - printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); + printf (" %s\n", "-E, --exact-match"); + printf (" %s\n", _("For paths or partitions specified with -p, only check for exact paths")); + printf (" %s\n", "-e, --errors-only"); + printf (" %s\n", _("Display only devices/mountpoints with errors")); + printf (" %s\n", "-g, --group=NAME"); + printf (" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); printf (" %s\n", "-k, --kilobytes"); printf (" %s\n", _("Same as '--units kB'")); - printf (" %s\n", "-m, --megabytes"); - printf (" %s\n", _("Same as '--units MB'")); printf (" %s\n", "-l, --local"); printf (" %s\n", _("Only check local filesystems")); - printf (" %s\n", "-p, --path=PATH, --partition=PARTITION"); - printf (" %s\n", _("Path or partition (may be repeated)")); - printf (" %s\n", "-x, --exclude_device=PATH "); - printf (" %s\n", _("Ignore device (only works if -p unspecified)")); - printf (" %s\n", _("-X, --exclude-type=TYPE ")); - printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)")); - printf (" %s\n", "-m, --mountpoint"); + printf (" %s\n", "-L, --stat-remote-fs"); + printf (" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); + printf (" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); + printf (" %s\n", "-M, --mountpoint"); printf (" %s\n", _("Display the mountpoint instead of the partition")); - printf (" %s\n", "-e, --errors-only"); - printf (" %s\n", _("Display only devices/mountpoints with errors")); + printf (" %s\n", "-m, --megabytes"); + printf (" %s\n", _("Same as '--units MB'")); + printf (" %s\n", "-A, --all"); + printf (" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); + printf (" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); + printf (" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)")); + printf (" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION"); + printf (" %s\n", _("Regular expression for path or partition (may be repeated)")); + printf (" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION"); + printf (" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)")); + printf (" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION"); + printf (" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)")); printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT); + printf (" %s\n", "-u, --units=STRING"); + printf (" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); printf (_(UT_VERBOSE)); + printf (" %s\n", "-X, --exclude-type=TYPE"); + printf (" %s\n", _("Ignore all filesystems of indicated type (may be repeated)")); + +#ifdef NP_EXTRA_OPTS + printf ("\n"); + printf ("%s\n", _("Notes:")); + printf (_(UT_EXTRA_OPTS_NOTES)); +#endif + printf ("\n"); printf ("%s\n", _("Examples:")); printf (" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /"); printf (" %s\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB")); + printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -g sidDATA -r '^/oracle/SID/data.*$'"); + printf (" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); + printf (" %s\n", _("are grouped which means the freespace thresholds are applied to all disks together")); + printf (" %s\n", "check_disk -w 100M -c 50M -C -w 1000M -c 500M -p /foo -C -w 5% -c 3% -p /bar"); + printf (" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); + printf (_(UT_SUPPORT)); } @@ -760,6 +984,21 @@ void print_usage (void) { printf (_("Usage:")); - printf (" %s -w limit -c limit [-p path | -x device] [-t timeout]", progname); - printf ("[-m] [-e] [-W limit] [-K limit] [-v] [-q]\n"); + printf (" %s -w limit -c limit [-W limit] [-K limit] {-p path | -x device}\n", progname); + printf ("[-C] [-E] [-e] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); + printf ("[-t timeout] [-u unit] [-v] [-X type]\n"); +} + +void +stat_path (struct parameter_list *p) +{ + /* Stat entry to check that dir exists and is accessible */ + if (verbose >= 3) + printf("calling stat on %s\n", p->name); + if (stat (p->name, &stat_buf[0])) { + if (verbose >= 3) + printf("stat failed on %s\n", p->name); + printf("DISK %s - ", _("CRITICAL")); + die (STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); + } }