From 908aed4e6f9072e601a189d4ceff3152bdecc49d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:37:48 +0200 Subject: Refactor check_disk and library functions --- plugins/check_disk.c | 1069 +++++++++++++++++++++++++++----------------------- 1 file changed, 577 insertions(+), 492 deletions(-) (limited to 'plugins/check_disk.c') diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 050298d6..3cab816d 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -33,6 +33,10 @@ const char *email = "devel@monitoring-plugins.org"; #include "states.h" #include "common.h" +#include "output.h" +#include "perfdata.h" +#include "utils_base.h" +#include "lib/thresholds.h" #ifdef HAVE_SYS_STAT_H # include @@ -48,10 +52,9 @@ const char *email = "devel@monitoring-plugins.org"; #include #include "./popen.h" #include "./utils.h" -#include "./check_disk.d/utils_disk.h" #include "../gl/fsusage.h" #include "../gl/mountlist.h" -#include "check_disk.d/config.h" +#include "./check_disk.d/utils_disk.h" #if HAVE_LIMITS_H # include @@ -74,20 +77,22 @@ typedef struct { check_disk_config config; } check_disk_config_wrapper; static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static void set_all_thresholds(struct parameter_list *path, char * /*warn_freespace_units*/, char * /*crit_freespace_units*/, - char * /*warn_freespace_percent*/, char * /*crit_freespace_percent*/, char * /*warn_usedspace_units*/, - char * /*crit_usedspace_units*/, char * /*warn_usedspace_percent*/, char * /*crit_usedspace_percent*/, - char * /*warn_usedinodes_percent*/, char * /*crit_usedinodes_percent*/, char * /*warn_freeinodes_percent*/, - char * /*crit_freeinodes_percent*/); -static void print_help(void); -void print_usage(void); + +static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units, + char *warn_freespace_percent, char *crit_freespace_percent, char *warn_freeinodes_percent, + char *crit_freeinodes_percent); static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/); -static bool stat_path(struct parameter_list * /*parameters*/, bool /*ignore_missing*/); -static void get_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp, bool /*ignore_missing*/, - bool /*freespace_ignore_reserved*/, uintmax_t /*mult*/, struct parameter_list * /*path_select_list*/, - struct name_list * /*seen*/); -static void get_path_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp, bool /*freespace_ignore_reserved*/, - uintmax_t /*mult*/, struct name_list * /*seen*/); +static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/); + +/* + * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control how reserved + * and inodes should be judged (ignored or not) + */ +static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp, bool freespace_ignore_reserved); +static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit); + +void print_usage(void); +static void print_help(void); static int verbose = 0; @@ -100,7 +105,7 @@ int main(int argc, char **argv) { char mountdir[32]; #endif - /* Parse extra opts if any */ + // Parse extra opts if any argv = np_extra_opts(&argc, argv, progname); check_disk_config_wrapper tmp_config = process_arguments(argc, argv); @@ -110,289 +115,222 @@ int main(int argc, char **argv) { check_disk_config config = tmp_config.config; - /* If a list of paths has not been selected, find entire - mount list and create list of paths - */ - if (!config.path_selected && !config.path_ignored) { - for (struct mount_entry *me = config.mount_list; me; me = me->me_next) { - struct parameter_list *path = NULL; - if (!(path = np_find_parameter(config.path_select_list, me->me_mountdir))) { - path = np_add_parameter(&config.path_select_list, me->me_mountdir); - } - path->best_match = me; - path->group = config.group; - set_all_thresholds(path, config.warn_freespace_units, config.crit_freespace_units, config.warn_freespace_percent, - config.crit_freespace_percent, config.warn_usedspace_units, config.crit_usedspace_units, - config.warn_usedspace_percent, config.crit_usedspace_percent, config.warn_usedinodes_percent, - config.crit_usedinodes_percent, config.warn_freeinodes_percent, config.crit_freeinodes_percent); - } + if (config.output_format_is_set) { + mp_set_format(config.output_format); } - if (!config.path_ignored) { - np_set_best_match(config.path_select_list, config.mount_list, config.exact_match); + if (config.erronly) { + mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY); } - /* Error if no match found for specified paths */ - struct parameter_list *temp_list = config.path_select_list; + if (!config.path_ignored) { + mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list, config.exact_match); + } - char *ignored = strdup(""); - while (config.path_select_list) { - if (!config.path_select_list->best_match && config.ignore_missing) { - /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ - if (config.path_select_list == temp_list) { - temp_list = config.path_select_list->name_next; - } - /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */ - xasprintf(&ignored, "%s %s;", ignored, config.path_select_list->name); + // Error if no match found for specified paths + for (parameter_list_elem *elem = config.path_select_list.first; elem;) { + if (!elem->best_match && config.ignore_missing) { /* Delete the path from the list so that it is not stat-checked later in the code. */ - config.path_select_list = np_del_parameter(config.path_select_list, config.path_select_list->name_prev); - } else if (!config.path_select_list->best_match) { + elem = mp_int_fs_list_del(&config.path_select_list, elem); + continue; + } + if (!elem->best_match) { /* Without --ignore-missing option, exit with Critical state. */ - die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), config.path_select_list->name); - } else { - /* Continue jumping through the list */ - config.path_select_list = config.path_select_list->name_next; + die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name); } - } - config.path_select_list = temp_list; - - mp_state_enum result = STATE_UNKNOWN; - if (!config.path_select_list && config.ignore_missing) { - result = STATE_OK; - if (verbose >= 2) { - printf("None of the provided paths were found\n"); - } + elem = mp_int_fs_list_get_next(elem); } - mp_state_enum filesystem_result = STATE_UNKNOWN; - char *perf = strdup(""); - char *perf_ilabel = strdup(""); - char *output = strdup(""); - struct parameter_list *path = NULL; - /* Process for every path in list */ - for (path = config.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); + mp_check overall = mp_check_init(); + if (config.path_select_list.length == 0) { + mp_subcheck none_sc = mp_subcheck_init(); + xasprintf(&none_sc.output, "No filesystems were found for the provided parameters"); + if (config.ignore_missing) { + none_sc = mp_set_subcheck_state(none_sc, STATE_OK); + } else { + none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN); + if (verbose >= 2) { + printf("None of the provided paths were found\n"); + } } + mp_add_subcheck_to_check(&overall, none_sc); + mp_exit(overall); + } - if (verbose >= 3 && path->group != NULL) { - printf("Group of %s: %s\n", path->name, path->group); + // Filter list first + for (parameter_list_elem *path = config.path_select_list.first; path;) { + if (!path->best_match) { + path = mp_int_fs_list_del(&config.path_select_list, path); + continue; } - // reset filesystem result - filesystem_result = STATE_UNKNOWN; - struct mount_entry *mount_entry = path->best_match; - if (!mount_entry) { - continue; - } - #ifdef __CYGWIN__ if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) { + path = mp_int_fs_list_del(&config.path_select_list, path); continue; } + + char *mountdir = NULL; snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); if (GetDriveType(mountdir) != DRIVE_FIXED) { - me->me_remote = 1; + mount_entry->me_remote = 1; } #endif - /* Filters */ /* Remove filesystems already seen */ if (np_seen_name(config.seen, mount_entry->me_mountdir)) { + path = mp_int_fs_list_del(&config.path_select_list, path); continue; } - np_add_name(&config.seen, mount_entry->me_mountdir); if (path->group == NULL) { - /* Skip remote filesystems if we're not interested in them */ - if (mount_entry->me_remote && config.show_local_fs) { - if (config.stat_remote_fs) { - if (!stat_path(path, config.ignore_missing) && config.ignore_missing) { - result = STATE_OK; - xasprintf(&ignored, "%s %s;", ignored, path->name); - } - } - continue; - /* Skip pseudo fs's if we haven't asked for all fs's */ - } if (config.fs_exclude_list && np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) { + // Skip excluded fs's + path = mp_int_fs_list_del(&config.path_select_list, path); continue; - /* Skip excluded fs's */ } + if (config.device_path_exclude_list && (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) || np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) { + // Skip excluded device or mount paths + path = mp_int_fs_list_del(&config.path_select_list, path); continue; - /* Skip not included fstypes */ } + if (config.fs_include_list && !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) { + // Skip not included fstypes + path = mp_int_fs_list_del(&config.path_select_list, path); continue; } - } - - if (!stat_path(path, config.ignore_missing)) { - if (config.ignore_missing) { - result = STATE_OK; - xasprintf(&ignored, "%s %s;", ignored, path->name); - } - continue; - } - struct fs_usage fsp = {0}; - get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp); - - if (fsp.fsu_blocks && strcmp("none", mount_entry->me_mountdir)) { - get_stats(path, &fsp, config.ignore_missing, config.freespace_ignore_reserved, config.mult, config.path_select_list, - config.seen); - - if (verbose >= 3) { - printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f " - "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n", - mount_entry->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, config.mult); - } - - /* Threshold comparisons */ - - mp_state_enum temp_result = get_status(path->dfree_units, path->freespace_units); - if (verbose >= 3) { - printf("Freespace_units result=%d\n", temp_result); + /* Skip remote filesystems if we're not interested in them */ + if (mount_entry->me_remote && config.show_local_fs) { + if (config.stat_remote_fs) { + // TODO Stat here + if (!stat_path(path, config.ignore_missing) && config.ignore_missing) { + } + } + continue; } - filesystem_result = max_state(filesystem_result, temp_result); - temp_result = get_status(path->dfree_pct, path->freespace_percent); - if (verbose >= 3) { - printf("Freespace%% result=%d\n", temp_result); + // TODO why stat here? remove unstatable fs? + if (!stat_path(path, config.ignore_missing)) { + // if (config.ignore_missing) { + // xasprintf(&ignored, "%s %s;", ignored, path->name); + // } + // not accessible, remove from list + path = mp_int_fs_list_del(&config.path_select_list, path); + continue; } - filesystem_result = max_state(filesystem_result, temp_result); + } - temp_result = get_status(path->dused_units, path->usedspace_units); - if (verbose >= 3) { - printf("Usedspace_units result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); + path = mp_int_fs_list_get_next(path); + } - temp_result = get_status(path->dused_pct, path->usedspace_percent); - if (verbose >= 3) { - printf("Usedspace_percent result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); + // now get the actual measurements + for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) { + // Get actual metrics here + struct mount_entry *mount_entry = filesystem->best_match; + struct fs_usage fsp = {0}; + get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp); - temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent); - if (verbose >= 3) { - printf("Usedinodes_percent result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); + if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) { + *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved); - temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent); if (verbose >= 3) { - printf("Freeinodes_percent result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); - - result = max_state(result, filesystem_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 */ - uint64_t warning_high_tide = UINT64_MAX; - - if (path->freespace_units->warning != NULL) { - warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * config.mult; - } - if (path->freespace_percent->warning != NULL) { - warning_high_tide = min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * - (path->dtotal_units * config.mult))); - } - - uint64_t critical_high_tide = UINT64_MAX; - - if (path->freespace_units->critical != NULL) { - critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * config.mult; - } - if (path->freespace_percent->critical != NULL) { - critical_high_tide = min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * - (path->dtotal_units * config.mult))); - } - - /* Nb: *_high_tide are unset when == UINT64_MAX */ - xasprintf(&perf, "%s %s", perf, - perfdata_uint64((!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname - : mount_entry->me_mountdir, - path->dused_units * config.mult, "B", (warning_high_tide != UINT64_MAX), warning_high_tide, - (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, - path->dtotal_units * config.mult)); - - if (config.display_inodes_perfdata) { - /* *_high_tide must be reinitialized at each run */ - warning_high_tide = UINT64_MAX; - critical_high_tide = UINT64_MAX; - - if (path->freeinodes_percent->warning != NULL) { - warning_high_tide = (uint64_t)fabs( - min((double)warning_high_tide, (double)(1.0 - path->freeinodes_percent->warning->end / 100) * path->inodes_total)); - } - if (path->freeinodes_percent->critical != NULL) { - critical_high_tide = (uint64_t)fabs(min( - (double)critical_high_tide, (double)(1.0 - path->freeinodes_percent->critical->end / 100) * path->inodes_total)); - } - - xasprintf(&perf_ilabel, "%s (inodes)", - (!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname - : mount_entry->me_mountdir); - /* Nb: *_high_tide are unset when == UINT64_MAX */ - xasprintf(&perf, "%s %s", perf, - perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX), warning_high_tide, - (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, path->inodes_total)); + printf("For %s, used_units=%lu free_units=%lu total_units=%lu " + "fsp.fsu_blocksize=%lu\n", + mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes, filesystem->total_bytes, + fsp.fsu_blocksize); } + } else { + // failed to retrieve file system data or not mounted? + filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem); + continue; + } + filesystem = mp_int_fs_list_get_next(filesystem); + } - if (filesystem_result == STATE_OK && config.erronly && !verbose) { - continue; + if (verbose > 2) { + for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; + filesystem = mp_int_fs_list_get_next(filesystem)) { + assert(filesystem->best_match != NULL); + if (filesystem->best_match == NULL) { + printf("Filesystem path %s has no mount_entry!\n", filesystem->name); + } else { + // printf("Filesystem path %s has a mount_entry!\n", filesystem->name); } + } + } - char *flag_header = NULL; - if (filesystem_result && verbose >= 1) { - xasprintf(&flag_header, " %s [", state_text(filesystem_result)); + measurement_unit_list *measurements = NULL; + measurement_unit_list *current = NULL; + // create measuring units, because of groups + for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; filesystem = mp_int_fs_list_get_next(filesystem)) { + assert(filesystem->best_match != NULL); + + if (filesystem->group == NULL) { + // create a measurement unit for the fs + measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + if (measurements == NULL) { + measurements = current = add_measurement_list(NULL, unit); } else { - xasprintf(&flag_header, ""); + current = add_measurement_list(measurements, unit); } - xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, - (!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname - : mount_entry->me_mountdir, - path->dfree_units, config.units, path->dfree_pct); - if (path->dused_inodes_percent < 0) { - xasprintf(&output, "%s inode=-)%s;", output, (filesystem_result ? "]" : "")); + } else { + // Grouped elements are consecutive + if (measurements == NULL) { + // first entry + measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + unit.name = strdup(filesystem->group); + measurements = current = add_measurement_list(NULL, unit); } else { - xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, - ((filesystem_result && verbose >= 1) ? "]" : "")); + // if this is the first element of a group, the name of the previos entry is different + if (strcmp(filesystem->group, current->unit.name) != 0) { + // so, this must be the first element of a group + measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + unit.name = filesystem->group; + current = add_measurement_list(measurements, unit); + + } else { + // NOT the first entry of a group, add info to the other one + current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem); + } } - free(flag_header); } } - char *preamble = " - free space:"; - if (strcmp(output, "") == 0 && !config.erronly) { - preamble = ""; - xasprintf(&output, " - No disks were found for provided parameters"); + /* Process for every path in list */ + if (measurements != NULL) { + for (measurement_unit_list *unit = measurements; unit; unit = unit->next) { + mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata, config.display_unit); + mp_add_subcheck_to_check(&overall, unit_sc); + } + } else { + // Aparently no machting fs found + mp_subcheck none_sc = mp_subcheck_init(); + xasprintf(&none_sc.output, "No filesystems were found for the provided parameters"); + + if (config.ignore_missing) { + none_sc = mp_set_subcheck_state(none_sc, STATE_OK); + } else { + none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN); + } + mp_add_subcheck_to_check(&overall, none_sc); } - char *ignored_preamble = " - ignored paths:"; - printf("DISK %s%s%s%s%s|%s\n", state_text(result), (config.erronly && (result == STATE_OK)) ? "" : preamble, output, - (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf); - return result; + mp_exit(overall); } double calculate_percent(uintmax_t value, uintmax_t total) { double pct = -1; if (value <= DBL_MAX && total != 0) { - pct = (double)value / total * 100.0; + pct = (double)value / (double)total * 100.0; } + return pct; } @@ -409,11 +347,15 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { return result; } + enum { + output_format_index = CHAR_MAX + 1, + display_unit_index, + }; + static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"iwarning", required_argument, 0, 'W'}, - /* Dang, -C is taken. We might want to reshuffle this. */ {"icritical", required_argument, 0, 'K'}, {"kilobytes", no_argument, 0, 'k'}, {"megabytes", no_argument, 0, 'm'}, @@ -446,6 +388,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { {"clear", no_argument, 0, 'C'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"output-format", required_argument, 0, output_format_index}, + {"display-unit", required_argument, 0, display_unit_index}, {0, 0, 0, 0}}; for (int index = 1; index < argc; index++) { @@ -456,6 +400,17 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { int cflags = REG_NOSUB | REG_EXTENDED; int default_cflags = cflags; + char *warn_freespace_units = NULL; + char *crit_freespace_units = NULL; + char *warn_freespace_percent = NULL; + char *crit_freespace_percent = NULL; + char *warn_freeinodes_percent = NULL; + char *crit_freeinodes_percent = NULL; + + bool path_selected = false; + char *group = NULL; + byte_unit unit = MebiBytes; + result.config.mount_list = read_file_system_list(false); np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED); @@ -485,24 +440,24 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (strstr(optarg, "%")) { if (*optarg == '@') { - result.config.warn_freespace_percent = optarg; + warn_freespace_percent = optarg; } else { - xasprintf(&result.config.warn_freespace_percent, "@%s", optarg); + xasprintf(&warn_freespace_percent, "@%s", optarg); } } else { if (*optarg == '@') { - result.config.warn_freespace_units = optarg; + warn_freespace_units = optarg; } else { - xasprintf(&result.config.warn_freespace_units, "@%s", optarg); + xasprintf(&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 artificially - force @ at the beginning of the range, so that it is backwards compatible - */ + * 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 artificially + * force @ at the beginning of the range, so that it is backwards compatible + */ case 'c': /* critical threshold */ if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); @@ -510,84 +465,92 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (strstr(optarg, "%")) { if (*optarg == '@') { - result.config.crit_freespace_percent = optarg; + crit_freespace_percent = optarg; } else { - xasprintf(&result.config.crit_freespace_percent, "@%s", optarg); + xasprintf(&crit_freespace_percent, "@%s", optarg); } } else { if (*optarg == '@') { - result.config.crit_freespace_units = optarg; + crit_freespace_units = optarg; } else { - xasprintf(&result.config.crit_freespace_units, "@%s", optarg); + xasprintf(&crit_freespace_units, "@%s", optarg); } } break; case 'W': /* warning inode threshold */ if (*optarg == '@') { - result.config.warn_freeinodes_percent = optarg; + warn_freeinodes_percent = optarg; } else { - xasprintf(&result.config.warn_freeinodes_percent, "@%s", optarg); + xasprintf(&warn_freeinodes_percent, "@%s", optarg); } break; case 'K': /* critical inode threshold */ if (*optarg == '@') { - result.config.crit_freeinodes_percent = optarg; + crit_freeinodes_percent = optarg; } else { - xasprintf(&result.config.crit_freeinodes_percent, "@%s", optarg); + xasprintf(&crit_freeinodes_percent, "@%s", optarg); } break; case 'u': - free(result.config.units); if (!strcasecmp(optarg, "bytes")) { - result.config.mult = (uintmax_t)1; - result.config.units = strdup("B"); + unit = Bytes; } else if (!strcmp(optarg, "KiB")) { - result.config.mult = (uintmax_t)1024; - result.config.units = strdup("KiB"); + unit = KibiBytes; } else if (!strcmp(optarg, "kB")) { - result.config.mult = (uintmax_t)1000; - result.config.units = strdup("kB"); + unit = KiloBytes; } else if (!strcmp(optarg, "MiB")) { - result.config.mult = (uintmax_t)1024 * 1024; - result.config.units = strdup("MiB"); + unit = MebiBytes; } else if (!strcmp(optarg, "MB")) { - result.config.mult = (uintmax_t)1000 * 1000; - result.config.units = strdup("MB"); + unit = MegaBytes; } else if (!strcmp(optarg, "GiB")) { - result.config.mult = (uintmax_t)1024 * 1024 * 1024; - result.config.units = strdup("GiB"); + unit = GibiBytes; } else if (!strcmp(optarg, "GB")) { - result.config.mult = (uintmax_t)1000 * 1000 * 1000; - result.config.units = strdup("GB"); + unit = GigaBytes; } else if (!strcmp(optarg, "TiB")) { - result.config.mult = (uintmax_t)1024 * 1024 * 1024 * 1024; - result.config.units = strdup("TiB"); + unit = TebiBytes; } else if (!strcmp(optarg, "TB")) { - result.config.mult = (uintmax_t)1000 * 1000 * 1000 * 1000; - result.config.units = strdup("TB"); + unit = TeraBytes; } else if (!strcmp(optarg, "PiB")) { - result.config.mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; - result.config.units = strdup("PiB"); + unit = PebiBytes; } else if (!strcmp(optarg, "PB")) { - result.config.mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; - result.config.units = strdup("PB"); + unit = PetaBytes; } else { die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); } - if (result.config.units == NULL) { - die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units"); - } break; - case 'k': /* display mountpoint */ - result.config.mult = 1024; - free(result.config.units); - result.config.units = strdup("kiB"); + case 'k': + unit = KibiBytes; break; - case 'm': /* display mountpoint */ - result.config.mult = 1024 * 1024; - free(result.config.units); - result.config.units = strdup("MiB"); + case 'm': + unit = MebiBytes; + break; + case display_unit_index: + if (!strcasecmp(optarg, "bytes")) { + result.config.display_unit = Bytes; + } else if (!strcmp(optarg, "KiB")) { + result.config.display_unit = KibiBytes; + } else if (!strcmp(optarg, "kB")) { + result.config.display_unit = KiloBytes; + } else if (!strcmp(optarg, "MiB")) { + result.config.display_unit = MebiBytes; + } else if (!strcmp(optarg, "MB")) { + result.config.display_unit = MegaBytes; + } else if (!strcmp(optarg, "GiB")) { + result.config.display_unit = GibiBytes; + } else if (!strcmp(optarg, "GB")) { + result.config.display_unit = GigaBytes; + } else if (!strcmp(optarg, "TiB")) { + result.config.display_unit = TebiBytes; + } else if (!strcmp(optarg, "TB")) { + result.config.display_unit = TeraBytes; + } else if (!strcmp(optarg, "PiB")) { + result.config.display_unit = PebiBytes; + } else if (!strcmp(optarg, "PB")) { + result.config.display_unit = PetaBytes; + } else { + die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); + } break; case 'L': result.config.stat_remote_fs = true; @@ -599,43 +562,34 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.display_inodes_perfdata = true; break; case 'p': /* select path */ { - if (!(result.config.warn_freespace_units || result.config.crit_freespace_units || result.config.warn_freespace_percent || - result.config.crit_freespace_percent || result.config.warn_usedspace_units || result.config.crit_usedspace_units || - result.config.warn_usedspace_percent || result.config.crit_usedspace_percent || result.config.warn_usedinodes_percent || - result.config.crit_usedinodes_percent || result.config.warn_freeinodes_percent || - result.config.crit_freeinodes_percent)) { + if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_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 */ - struct parameter_list *se; - if (!(se = np_find_parameter(result.config.path_select_list, optarg))) { - se = np_add_parameter(&result.config.path_select_list, optarg); - - struct stat stat_buf = {}; - if (stat(optarg, &stat_buf) && result.config.ignore_missing) { - result.config.path_ignored = true; - break; - } + parameter_list_elem *search_entry; + if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) { + search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg); + + // struct stat stat_buf = {}; + // if (stat(optarg, &stat_buf) && result.config.ignore_missing) { + // result.config.path_ignored = true; + // break; + // } } - se->group = result.config.group; - set_all_thresholds( - se, result.config.warn_freespace_units, result.config.crit_freespace_units, result.config.warn_freespace_percent, - result.config.crit_freespace_percent, result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, result.config.warn_usedinodes_percent, - result.config.crit_usedinodes_percent, result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + search_entry->group = group; + set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + + warn_freeinodes_percent, crit_freeinodes_percent); /* With autofs, it is required to stat() the path before re-populating the mount_list */ - if (!stat_path(se, result.config.ignore_missing)) { - break; - } - /* 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 */ - result.config.mount_list = read_file_system_list(false); - np_set_best_match(se, result.config.mount_list, result.config.exact_match); + // if (!stat_path(se, result.config.ignore_missing)) { + // break; + // } + mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); - result.config.path_selected = true; + path_selected = true; } break; case 'x': /* exclude path or partition */ np_add_name(&result.config.device_path_exclude_list, optarg); @@ -667,7 +621,7 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.erronly = true; break; case 'E': - if (result.config.path_selected) { + if (path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); } result.config.exact_match = true; @@ -676,16 +630,16 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.freespace_ignore_reserved = true; break; case 'g': - if (result.config.path_selected) { + if (path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); } - result.config.group = optarg; + group = optarg; break; case 'I': cflags |= REG_ICASE; // Intentional fallthrough case 'i': { - if (!result.config.path_selected) { + 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")); } @@ -697,29 +651,20 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } - struct parameter_list *temp_list = result.config.path_select_list; - struct parameter_list *previous = NULL; - while (temp_list) { - if (temp_list->best_match) { - if (np_regex_match_mount_entry(temp_list->best_match, ®ex)) { + for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) { + if (elem->best_match) { + if (np_regex_match_mount_entry(elem->best_match, ®ex)) { if (verbose >= 3) { - printf("ignoring %s matching regex\n", temp_list->name); + printf("ignoring %s matching regex\n", elem->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) { - result.config.path_select_list = temp_list; - } - } else { - previous = temp_list; - temp_list = temp_list->name_next; + elem = mp_int_fs_list_del(&result.config.path_select_list, elem); + continue; } - } else { - previous = temp_list; - temp_list = temp_list->name_next; } + + elem = mp_int_fs_list_get_next(elem); } cflags = default_cflags; @@ -734,11 +679,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { cflags |= REG_ICASE; // Intentional fallthrough case 'r': { - if (!(result.config.warn_freespace_units || result.config.crit_freespace_units || result.config.warn_freespace_percent || - result.config.crit_freespace_percent || result.config.warn_usedspace_units || result.config.crit_usedspace_units || - result.config.warn_usedspace_percent || result.config.crit_usedspace_percent || result.config.warn_usedinodes_percent || - result.config.crit_usedinodes_percent || result.config.warn_freeinodes_percent || - result.config.crit_freeinodes_percent)) { + if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || + warn_freeinodes_percent || crit_freeinodes_percent)) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); } @@ -760,31 +702,28 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { } /* add parameter if not found. overwrite thresholds if path has already been added */ - struct parameter_list *se = NULL; - if (!(se = np_find_parameter(result.config.path_select_list, me->me_mountdir))) { - se = np_add_parameter(&result.config.path_select_list, me->me_mountdir); + parameter_list_elem *se = NULL; + if (!(se = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { + se = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); } - se->group = result.config.group; - set_all_thresholds(se, result.config.warn_freespace_units, result.config.crit_freespace_units, - result.config.warn_freespace_percent, result.config.crit_freespace_percent, - result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, - result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, - result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + se->group = group; + set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); } } - if (!found && result.config.ignore_missing) { - result.config.path_ignored = true; - result.config.path_selected = true; - break; - } if (!found) { + if (result.config.ignore_missing) { + result.config.path_ignored = true; + path_selected = true; + break; + } + die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); } - result.config.path_selected = true; - np_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); + path_selected = true; + mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); cflags = default_cflags; } break; @@ -793,37 +732,28 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { break; case 'C': { /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ - if (!result.config.path_selected) { - struct parameter_list *path; + if (!path_selected) { + parameter_list_elem *path; for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { - if (!(path = np_find_parameter(result.config.path_select_list, me->me_mountdir))) { - path = np_add_parameter(&result.config.path_select_list, me->me_mountdir); + if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { + path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); } path->best_match = me; - path->group = result.config.group; - set_all_thresholds(path, result.config.warn_freespace_units, result.config.crit_freespace_units, - result.config.warn_freespace_percent, result.config.crit_freespace_percent, - result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, - result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, - result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + path->group = group; + set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); } } - result.config.warn_freespace_units = NULL; - result.config.crit_freespace_units = NULL; - result.config.warn_usedspace_units = NULL; - result.config.crit_usedspace_units = NULL; - result.config.warn_freespace_percent = NULL; - result.config.crit_freespace_percent = NULL; - result.config.warn_usedspace_percent = NULL; - result.config.crit_usedspace_percent = NULL; - result.config.warn_usedinodes_percent = NULL; - result.config.crit_usedinodes_percent = NULL; - result.config.warn_freeinodes_percent = NULL; - result.config.crit_freeinodes_percent = NULL; - - result.config.path_selected = false; - result.config.group = NULL; + + warn_freespace_units = NULL; + crit_freespace_units = NULL; + warn_freespace_percent = NULL; + crit_freespace_percent = NULL; + warn_freeinodes_percent = NULL; + crit_freeinodes_percent = NULL; + + path_selected = false; + group = NULL; } break; case 'V': /* version */ print_revision(progname, NP_VERSION); @@ -833,68 +763,145 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { exit(STATE_UNKNOWN); case '?': /* help */ usage(_("Unknown argument")); + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } } } /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ int index = optind; - if (result.config.warn_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (argc > index && is_intnonneg(argv[index])) { if (verbose > 0) { printf("Got an positional warn threshold: %s\n", argv[index]); } - result.config.warn_usedspace_percent = argv[index++]; + char *range = argv[index++]; + mp_range_parsed tmp = mp_parse_range_string(range); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold"); + } + + mp_range tmp_range = tmp.range; + // Invert range to use it for free instead of used + // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range; + + warn_freespace_percent = mp_range_to_string(tmp_range); + + if (verbose > 0) { + printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent); + } } - if (result.config.crit_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (argc > index && is_intnonneg(argv[index])) { if (verbose > 0) { printf("Got an positional crit threshold: %s\n", argv[index]); } - result.config.crit_usedspace_percent = argv[index++]; + char *range = argv[index++]; + mp_range_parsed tmp = mp_parse_range_string(range); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold"); + } + + mp_range tmp_range = tmp.range; + // Invert range to use it for free instead of used + // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range; + + crit_freespace_percent = mp_range_to_string(tmp_range); + + if (verbose > 0) { + printf("Positional critical threshold transformed to: %s\n", crit_freespace_percent); + } } if (argc > index) { if (verbose > 0) { printf("Got an positional filesystem: %s\n", argv[index]); } - struct parameter_list *se = np_add_parameter(&result.config.path_select_list, strdup(argv[index++])); - result.config.path_selected = true; - set_all_thresholds(se, result.config.warn_freespace_units, result.config.crit_freespace_units, result.config.warn_freespace_percent, - result.config.crit_freespace_percent, result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, - result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, - result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + struct parameter_list *se = mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++])); + path_selected = true; + set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); } - if (result.config.units == NULL) { - result.config.units = strdup("MiB"); - result.config.mult = (uintmax_t)1024 * 1024; + // If a list of paths has not been explicitely selected, find entire + // mount list and create list of paths + if (!path_selected && !result.config.path_ignored) { + for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { + if (me->me_dummy != 0) { + // just do not add dummy filesystems + continue; + } + + parameter_list_elem *path = NULL; + if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { + path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); + } + path->best_match = me; + path->group = group; + set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); + } + } + + // Set thresholds to the appropriate unit + for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp; tmp = mp_int_fs_list_get_next(tmp)) { + + mp_perfdata_value factor = mp_create_pd_value(unit); + + if (tmp->freespace_units.critical_is_set) { + tmp->freespace_units.critical = mp_range_multiply(tmp->freespace_units.critical, factor); + } + if (tmp->freespace_units.warning_is_set) { + tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor); + } } return result; } -void set_all_thresholds(struct parameter_list *path, char *warn_freespace_units, char *crit_freespace_units, char *warn_freespace_percent, - char *crit_freespace_percent, char *warn_usedspace_units, char *crit_usedspace_units, char *warn_usedspace_percent, - char *crit_usedspace_percent, char *warn_usedinodes_percent, char *crit_usedinodes_percent, - char *warn_freeinodes_percent, char *crit_freeinodes_percent) { - free(path->freespace_units); - set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); +void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units, char *warn_freespace_percent, + char *crit_freespace_percent, char *warn_freeinodes_percent, char *crit_freeinodes_percent) { + mp_range_parsed tmp; + + if (warn_freespace_units) { + tmp = mp_parse_range_string(warn_freespace_units); + path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range); + } - free(path->freespace_percent); - set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); + if (crit_freespace_units) { + tmp = mp_parse_range_string(crit_freespace_units); + path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range); + } - free(path->usedspace_units); - set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); + if (warn_freespace_percent) { + tmp = mp_parse_range_string(warn_freespace_percent); + path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range); + } - free(path->usedspace_percent); - set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); + if (crit_freespace_percent) { + tmp = mp_parse_range_string(crit_freespace_percent); + path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range); + } - free(path->usedinodes_percent); - set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); + if (warn_freeinodes_percent) { + tmp = mp_parse_range_string(warn_freeinodes_percent); + path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range); + } - free(path->freeinodes_percent); - set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); + if (crit_freeinodes_percent) { + tmp = mp_parse_range_string(crit_freeinodes_percent); + path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range); + } } void print_help(void) { @@ -941,8 +948,6 @@ void print_help(void) { printf(" %s\n", _("Display inode usage in perfdata")); 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", "-l, --local"); printf(" %s\n", _("Only check local filesystems")); printf(" %s\n", "-L, --stat-remote-fs"); @@ -950,8 +955,6 @@ void print_help(void) { 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 (block) device instead of the mount point")); - 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"); @@ -967,12 +970,25 @@ void print_help(void) { printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(" %s\n", "-u, --units=STRING"); - printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); + printf(" %s\n", _("Select the unit used for the absolute value thresholds")); + printf( + " %s\n", + _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); + printf(" %s\n", "-k, --kilobytes"); + printf(" %s\n", _("Same as '--units kB'")); + printf(" %s\n", "--display-unit"); + printf(" %s\n", _("Select the unit used for in the output")); + printf( + " %s\n", + _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); + printf(" %s\n", "-m, --megabytes"); + printf(" %s\n", _("Same as '--units MB'")); printf(UT_VERBOSE); printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); printf(" %s\n", "-N, --include-type=TYPE_REGEX"); printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf("%s\n", _("General usage hints:")); @@ -1002,7 +1018,7 @@ void print_usage(void) { printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); } -bool stat_path(struct parameter_list *parameters, bool ignore_missing) { +bool stat_path(parameter_list_elem *parameters, bool ignore_missing) { /* Stat entry to check that dir exists and is accessible */ if (verbose >= 3) { printf("calling stat on %s\n", parameters->name); @@ -1023,97 +1039,166 @@ bool stat_path(struct parameter_list *parameters, bool ignore_missing) { return true; } -void get_stats(struct parameter_list *parameters, struct fs_usage *fsp, bool ignore_missing, bool freespace_ignore_reserved, uintmax_t mult, - struct parameter_list *path_select_list, struct name_list *seen) { - struct fs_usage tmpfsp; - bool first = true; - - if (parameters->group == NULL) { - get_path_stats(parameters, fsp, freespace_ignore_reserved, mult, seen); - } else { - /* find all group members */ - for (struct parameter_list *p_list = path_select_list; p_list; p_list = p_list->name_next) { - -#ifdef __CYGWIN__ - if (strncmp(p_list->name, "/cygdrive/", 10) != 0) { - continue; - } -#endif - - if (p_list->group && !(strcmp(p_list->group, parameters->group))) { - if (!stat_path(p_list, ignore_missing)) { - continue; - } - get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp); - get_path_stats(p_list, &tmpfsp, freespace_ignore_reserved, mult, seen); - if (verbose >= 3) { - printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n", - p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, - p_list->dfree_units, p_list->dtotal_units, mult); - } - - /* prevent counting the first FS of a group twice since its parameter_list entry - * is used to carry the information of all file systems of the entire group */ - if (!first) { - parameters->total += p_list->total; - parameters->available += p_list->available; - parameters->available_to_root += p_list->available_to_root; - parameters->used += p_list->used; - - parameters->dused_units += p_list->dused_units; - parameters->dfree_units += p_list->dfree_units; - parameters->dtotal_units += p_list->dtotal_units; - parameters->inodes_total += p_list->inodes_total; - parameters->inodes_free += p_list->inodes_free; - parameters->inodes_free_to_root += p_list->inodes_free_to_root; - parameters->inodes_used += p_list->inodes_used; - } - first = false; - } - if (verbose >= 3) { - printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", parameters->group, - parameters->dused_units, parameters->dfree_units, parameters->dtotal_units, tmpfsp.fsu_blocksize, mult); - } - } - /* modify devname and mountdir for output */ - parameters->best_match->me_mountdir = parameters->best_match->me_devname = parameters->group; - } - /* finally calculate percentages for either plain FS or summed up group */ - parameters->dused_pct = - calculate_percent(parameters->used, parameters->used + parameters->available); /* used + available can never be > uintmax */ - parameters->dfree_pct = 100.0 - parameters->dused_pct; - parameters->dused_inodes_percent = calculate_percent(parameters->inodes_total - parameters->inodes_free, parameters->inodes_total); - parameters->dfree_inodes_percent = 100 - parameters->dused_inodes_percent; -} +static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp, bool freespace_ignore_reserved) { + uintmax_t available = fsp.fsu_bavail; + uintmax_t available_to_root = fsp.fsu_bfree; + uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree; + uintmax_t total; -void get_path_stats(struct parameter_list *parameters, struct fs_usage *fsp, bool freespace_ignore_reserved, uintmax_t mult, - struct name_list *seen) { - parameters->available = fsp->fsu_bavail; - parameters->available_to_root = fsp->fsu_bfree; - parameters->used = fsp->fsu_blocks - fsp->fsu_bfree; if (freespace_ignore_reserved) { /* option activated : we subtract the root-reserved space from the total */ - parameters->total = fsp->fsu_blocks - parameters->available_to_root + parameters->available; + total = fsp.fsu_blocks - available_to_root + available; } else { /* default behaviour : take all the blocks into account */ - parameters->total = fsp->fsu_blocks; + total = fsp.fsu_blocks; } - parameters->dused_units = parameters->used * fsp->fsu_blocksize / mult; - parameters->dfree_units = parameters->available * fsp->fsu_blocksize / mult; - parameters->dtotal_units = parameters->total * fsp->fsu_blocksize / mult; + parameters.used_bytes = used * fsp.fsu_blocksize; + parameters.free_bytes = available * fsp.fsu_blocksize; + parameters.total_bytes = total * fsp.fsu_blocksize; + /* Free file nodes. Not sure the workaround is required, but in case...*/ - parameters->inodes_free = fsp->fsu_ffree; - parameters->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ - parameters->inodes_used = fsp->fsu_files - fsp->fsu_ffree; + parameters.inodes_free = fsp.fsu_ffree; + parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */ + parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree; + if (freespace_ignore_reserved) { /* option activated : we subtract the root-reserved inodes from the total */ /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ /* for others, fsp->fsu_ffree == fsp->fsu_favail */ - parameters->inodes_total = fsp->fsu_files - parameters->inodes_free_to_root + parameters->inodes_free; + parameters.inodes_total = fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free; } else { /* default behaviour : take all the inodes into account */ - parameters->inodes_total = fsp->fsu_files; + parameters.inodes_total = fsp.fsu_files; + } + + return parameters; +} + +mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit) { + mp_subcheck result = mp_subcheck_init(); + result = mp_set_subcheck_default_state(result, STATE_UNKNOWN); + xasprintf(&result.output, "%s", measurement_unit.name); + + if (!measurement_unit.is_group && measurement_unit.filesystem_type) { + xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type); + } + + /* Threshold comparisons */ + + // =============================== + // Free space absolute values test + mp_subcheck freespace_bytes_sc = mp_subcheck_init(); + freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK); + + if (unit != Humanized) { + xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)", (uintmax_t)(measurement_unit.free_bytes / unit), + get_unit_string(unit), (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit)); + } else { + xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)", humanize_byte_value(measurement_unit.free_bytes, false), + humanize_byte_value(measurement_unit.total_bytes, false)); } - np_add_name(&seen, parameters->best_match->me_mountdir); + + mp_perfdata used_space = perfdata_init(); + used_space.label = measurement_unit.name; + used_space.value = mp_create_pd_value(measurement_unit.free_bytes); + used_space = mp_set_pd_max_value(used_space, mp_create_pd_value(measurement_unit.total_bytes)); + used_space = mp_set_pd_min_value(used_space, mp_create_pd_value(0)); + used_space.uom = "B"; + used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); + freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(used_space)); + + // special case for absolute space thresholds here: + // if absolute values are not set, compute the thresholds from percentage thresholds + mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds; + if (!temp_thlds.critical_is_set && measurement_unit.freespace_percent_thresholds.critical_is_set) { + mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical; + + if (!tmp_range.end_infinity) { + tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes); + } + + if (!tmp_range.start_infinity) { + tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes); + } + measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range); + used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); + } + + if (!temp_thlds.warning_is_set && measurement_unit.freespace_percent_thresholds.warning_is_set) { + mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning; + if (!tmp_range.end_infinity) { + tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes); + } + if (!tmp_range.start_infinity) { + tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes); + } + measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range); + used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); + } + + mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space); + mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc); + + // ========================== + // Free space percentage test + mp_subcheck freespace_percent_sc = mp_subcheck_init(); + freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK); + + double free_percentage = calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes); + xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage); + + // Using perfdata here just to get to the test result + mp_perfdata free_space_percent_pd = perfdata_init(); + free_space_percent_pd.value = mp_create_pd_value(free_percentage); + free_space_percent_pd = mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds); + + freespace_percent_sc = mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd)); + mp_add_subcheck_to_subcheck(&result, freespace_percent_sc); + + // ================ + // Free inodes test + // Only ever useful if the number of inodes is static (e.g. ext4), + // not when it is dynamic (e.g btrfs) + // Assumption: if the total number of inodes == 0, we have such a case and just skip the test + if (measurement_unit.inodes_total > 0) { + mp_subcheck freeindodes_percent_sc = mp_subcheck_init(); + freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK); + + double free_inode_percentage = calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total); + + if (verbose > 0) { + printf("free inode percentage computed: %g\n", free_inode_percentage); + } + + xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)", free_inode_percentage, measurement_unit.inodes_free, + measurement_unit.inodes_total); + + mp_perfdata inodes_pd = perfdata_init(); + xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name); + inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used); + inodes_pd = mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total)); + inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0)); + + mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds; + + if (absolut_inode_thresholds.critical_is_set) { + absolut_inode_thresholds.critical = + mp_range_multiply(absolut_inode_thresholds.critical, mp_create_pd_value(measurement_unit.inodes_total / 100)); + } + if (absolut_inode_thresholds.warning_is_set) { + absolut_inode_thresholds.warning = + mp_range_multiply(absolut_inode_thresholds.warning, mp_create_pd_value(measurement_unit.inodes_total / 100)); + } + + inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds); + + freeindodes_percent_sc = mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd)); + if (display_inodes_perfdata) { + mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd); + } + mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc); + } + + return result; } -- cgit v1.2.3-74-g34f1