monitoring-plugins/plugins/check_disk.c
Lorenz Kästle c57381d789
Revert check_disk performance data back to used space (#2243)
* Implement simple output shortcut for ranges

If ranges start with zero (e.g. 0:10), the zero and the colon
can be left out.

This patch implements this by default, since some systems (icinga2)
do not fully implement the whole range format and this reduces errors
in the common case of just an upper border.

* switch check_disk perfdata back to used space
2026-04-06 11:55:27 +02:00

1342 lines
47 KiB
C

/*****************************************************************************
*
* Monitoring check_disk plugin
*
* License: GPL
* Copyright (c) 1999-2024 Monitoring 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 <http://www.gnu.org/licenses/>.
*
*
*****************************************************************************/
const char *progname = "check_disk";
const char *program_name = "check_disk"; /* Required for coreutils libs */
const char *copyright = "1999-2024";
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 <sys/stat.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <float.h>
#include "./popen.h"
#include "./utils.h"
#include "../gl/fsusage.h"
#include "../gl/mountlist.h"
#include "./check_disk.d/utils_disk.h"
#if HAVE_LIMITS_H
# include <limits.h>
#endif
#include "regex.h"
#ifdef __CYGWIN__
# include <windows.h>
# undef ERROR
# define ERROR -1
#endif
#ifdef _AIX
# pragma alloca
#endif
typedef struct {
int errorcode;
check_disk_config config;
} check_disk_config_wrapper;
static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
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(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;
// This would not be necessary in C23!!
const byte_unit Bytes_Factor = 1;
const byte_unit KibiBytes_factor = 1024;
const byte_unit MebiBytes_factor = 1048576;
const byte_unit GibiBytes_factor = 1073741824;
const byte_unit TebiBytes_factor = 1099511627776;
const byte_unit PebiBytes_factor = 1125899906842624;
const byte_unit ExbiBytes_factor = 1152921504606846976;
const byte_unit KiloBytes_factor = 1000;
const byte_unit MegaBytes_factor = 1000000;
const byte_unit GigaBytes_factor = 1000000000;
const byte_unit TeraBytes_factor = 1000000000000;
const byte_unit PetaBytes_factor = 1000000000000000;
const byte_unit ExaBytes_factor = 1000000000000000000;
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#ifdef __CYGWIN__
char mountdir[32];
#endif
// Parse extra opts if any
argv = np_extra_opts(&argc, argv, progname);
check_disk_config_wrapper tmp_config = process_arguments(argc, argv);
if (tmp_config.errorcode == ERROR) {
usage4(_("Could not parse arguments"));
}
check_disk_config config = tmp_config.config;
if (config.output_format_is_set) {
mp_set_format(config.output_format);
}
if (config.erronly) {
mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY);
}
if (!config.path_ignored) {
mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list,
config.exact_match);
}
// 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. */
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"), elem->name);
}
elem = mp_int_fs_list_get_next(elem);
}
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);
}
// 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;
}
struct mount_entry *mount_entry = path->best_match;
#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) {
mount_entry->me_remote = 1;
}
#endif
/* 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;
}
if (path->group == NULL) {
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;
}
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;
}
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;
}
/* 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;
}
// 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;
}
}
path = mp_int_fs_list_get_next(path);
}
// 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);
if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) {
*filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved);
if (verbose >= 3) {
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 (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);
}
}
}
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 {
current = add_measurement_list(measurements, unit);
}
} 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 {
// if this is the first element of a group, the name of the previous 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);
}
}
}
}
/* 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 {
// Apparently 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);
}
mp_exit(overall);
}
double calculate_percent(uintmax_t value, uintmax_t total) {
double pct = -1;
if (value <= DBL_MAX && total != 0) {
pct = (double)value / (double)total * 100.0;
}
return pct;
}
/* process command-line arguments */
check_disk_config_wrapper process_arguments(int argc, char **argv) {
check_disk_config_wrapper result = {
.errorcode = OK,
.config = check_disk_config_init(),
};
if (argc < 2) {
result.errorcode = ERROR;
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'},
{"icritical", required_argument, 0, 'K'},
{"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'},
{"include-type", required_argument, 0, 'N'},
{"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'},
{"freespace-ignore-reserved", no_argument, 0, 'f'},
{"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'},
{"ignore-missing", no_argument, 0, 'n'},
{"local", no_argument, 0, 'l'},
{"stat-remote-fs", no_argument, 0, 'L'},
{"iperfdata", no_argument, 0, 'P'},
{"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'},
{"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++) {
if (strcmp("-to", argv[index]) == 0) {
strcpy(argv[index], "-t");
}
}
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_factor;
result.config.mount_list = read_file_system_list(false);
np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED);
while (true) {
int option = 0;
int option_index = getopt_long(
argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
if (option_index == -1 || option_index == EOF) {
break;
}
switch (option_index) {
case 't': /* timeout period */
if (is_integer(optarg)) {
timeout_interval = atoi(optarg);
break;
} else {
usage2(_("Timeout interval must be a positive integer"), optarg);
}
/* See comments for 'c' */
case 'w': /* warning threshold */
if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
die(STATE_UNKNOWN, "Argument for --warning invalid or missing: %s\n", optarg);
}
if (strstr(optarg, "%")) {
if (*optarg == '@') {
warn_freespace_percent = optarg;
} else {
xasprintf(&warn_freespace_percent, "@%s", optarg);
}
} else {
if (*optarg == '@') {
warn_freespace_units = optarg;
} else {
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
*/
case 'c': /* critical threshold */
if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg);
}
if (strstr(optarg, "%")) {
if (*optarg == '@') {
crit_freespace_percent = optarg;
} else {
xasprintf(&crit_freespace_percent, "@%s", optarg);
}
} else {
if (*optarg == '@') {
crit_freespace_units = optarg;
} else {
xasprintf(&crit_freespace_units, "@%s", optarg);
}
}
break;
case 'W': /* warning inode threshold */
if (*optarg == '@') {
warn_freeinodes_percent = optarg;
} else {
xasprintf(&warn_freeinodes_percent, "@%s", optarg);
}
break;
case 'K': /* critical inode threshold */
if (*optarg == '@') {
crit_freeinodes_percent = optarg;
} else {
xasprintf(&crit_freeinodes_percent, "@%s", optarg);
}
break;
case 'u':
if (!strcasecmp(optarg, "bytes")) {
unit = Bytes_Factor;
} else if (!strcmp(optarg, "KiB")) {
unit = KibiBytes_factor;
} else if (!strcmp(optarg, "kB")) {
unit = KiloBytes_factor;
} else if (!strcmp(optarg, "MiB")) {
unit = MebiBytes_factor;
} else if (!strcmp(optarg, "MB")) {
unit = MegaBytes_factor;
} else if (!strcmp(optarg, "GiB")) {
unit = GibiBytes_factor;
} else if (!strcmp(optarg, "GB")) {
unit = GigaBytes_factor;
} else if (!strcmp(optarg, "TiB")) {
unit = TebiBytes_factor;
} else if (!strcmp(optarg, "TB")) {
unit = TeraBytes_factor;
} else if (!strcmp(optarg, "PiB")) {
unit = PebiBytes_factor;
} else if (!strcmp(optarg, "PB")) {
unit = PetaBytes_factor;
} else {
die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
}
break;
case 'k':
unit = KibiBytes_factor;
break;
case 'm':
unit = MebiBytes_factor;
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;
/* fallthrough */
case 'l':
result.config.show_local_fs = true;
break;
case 'P':
result.config.display_inodes_perfdata = true;
break;
case 'p': /* select path */ {
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 */
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;
// }
}
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;
// }
mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
result.config.exact_match);
path_selected = true;
} break;
case 'x': /* exclude path or partition */
np_add_name(&result.config.device_path_exclude_list, optarg);
break;
case 'X': /* exclude file system type */ {
int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED);
if (err != 0) {
char errbuf[MAX_INPUT_BUFFER];
regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
_("Could not compile regular expression"), errbuf);
}
break;
case 'N': /* include file system type */
err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED);
if (err != 0) {
char errbuf[MAX_INPUT_BUFFER];
regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
_("Could not compile regular expression"), errbuf);
}
} break;
case 'v': /* verbose */
verbose++;
break;
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 */
result.config.erronly = true;
break;
case 'e':
result.config.erronly = true;
break;
case 'E':
if (path_selected) {
die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
_("Must set -E before selecting paths\n"));
}
result.config.exact_match = true;
break;
case 'f':
result.config.freespace_ignore_reserved = 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;
// Intentional fallthrough
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"));
}
regex_t regex;
int err = regcomp(&regex, optarg, cflags);
if (err != 0) {
char errbuf[MAX_INPUT_BUFFER];
regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
_("Could not compile regular expression"), errbuf);
}
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, &regex)) {
if (verbose >= 3) {
printf("ignoring %s matching regex\n", elem->name);
}
elem = mp_int_fs_list_del(&result.config.path_select_list, elem);
continue;
}
}
elem = mp_int_fs_list_get_next(elem);
}
cflags = default_cflags;
} break;
case 'n':
result.config.ignore_missing = true;
break;
case 'A':
optarg = strdup(".*");
// Intentional fallthrough
case 'R':
cflags |= REG_ICASE;
// Intentional fallthrough
case 'r': {
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"));
}
regex_t regex;
int err = regcomp(&regex, optarg, cflags);
if (err != 0) {
char errbuf[MAX_INPUT_BUFFER];
regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
_("Could not compile regular expression"), errbuf);
}
bool found = false;
for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
if (np_regex_match_mount_entry(me, &regex)) {
found = 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 */
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 = 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) {
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);
}
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;
case 'M': /* display mountpoint */
result.config.display_mntp = true;
break;
case 'C': {
/* add all mount entries to path_select list if no partitions have been explicitly
* defined using -p */
if (!path_selected) {
parameter_list_elem *path;
for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
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);
}
}
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);
exit(STATE_UNKNOWN);
case 'h': /* help */
print_help();
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 (argc > index && is_intnonneg(argv[index])) {
if (verbose > 0) {
printf("Got an positional warn threshold: %s\n", argv[index]);
}
char *range = argv[index++];
mp_range_parsed tmp = mp_parse_range_string(range);
if (tmp.error != MP_PARSING_SUCCESS) {
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 (argc > index && is_intnonneg(argv[index])) {
if (verbose > 0) {
printf("Got an positional crit threshold: %s\n", argv[index]);
}
char *range = argv[index++];
mp_range_parsed tmp = mp_parse_range_string(range);
if (tmp.error != MP_PARSING_SUCCESS) {
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 =
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 a list of paths has not been explicitly 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(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);
tmp.range.start = mp_create_pd_value(0);
tmp.range.start_infinity = false;
path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range);
}
if (crit_freespace_units) {
tmp = mp_parse_range_string(crit_freespace_units);
tmp.range.start = mp_create_pd_value(0);
tmp.range.start_infinity = false;
path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range);
}
if (warn_freespace_percent) {
tmp = mp_parse_range_string(warn_freespace_percent);
tmp.range.start = mp_create_pd_value(0);
tmp.range.start_infinity = false;
path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range);
}
if (crit_freespace_percent) {
tmp = mp_parse_range_string(crit_freespace_percent);
tmp.range.start = mp_create_pd_value(0);
tmp.range.start_infinity = false;
path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range);
}
if (warn_freeinodes_percent) {
tmp = mp_parse_range_string(warn_freeinodes_percent);
tmp.range.start = mp_create_pd_value(0);
tmp.range.start_infinity = false;
path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range);
}
if (crit_freeinodes_percent) {
tmp = mp_parse_range_string(crit_freeinodes_percent);
tmp.range.start = mp_create_pd_value(0);
tmp.range.start_infinity = false;
path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range);
}
}
void print_help(void) {
print_revision(progname, NP_VERSION);
printf("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
printf(COPYRIGHT, copyright, email);
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", "-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 CRITICAL 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",
_("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
printf(" %s\n", "-x, --exclude_device=PATH <STRING>");
printf(" %s\n", _("Ignore device (only works if -p unspecified)"));
printf(" %s\n", "-C, --clear");
printf(" %s\n", _("Clear thresholds"));
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", "-f, --freespace-ignore-reserved");
printf(" %s\n", _("Don't account root-reserved blocks into freespace in perfdata"));
printf(" %s\n", "-P, --iperfdata");
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", "-l, --local");
printf(" %s\n", _("Only check local filesystems"));
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 (block) device instead of the mount point"));
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(" %s\n", "-n, --ignore-missing");
printf(" %s\n",
_("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
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", _("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:"));
printf(
" %s\n",
_("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} "
"{thresholds b} ...\""));
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\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
printf(" %s\n",
"check_disk -w 100 -c 50 -C -w 1000 -c 500 -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\n",
_("are grouped which means the freespace thresholds are applied to all disks together"));
printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -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);
}
void print_usage(void) {
printf("%s\n", _("Usage:"));
printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c "
"absolute_limit|-c percentage_limit%% | -K "
"inode_percentage_limit } {-p path | -x device}\n",
progname);
printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
}
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);
}
struct stat stat_buf = {0};
if (stat(parameters->name, &stat_buf)) {
if (verbose >= 3) {
printf("stat failed on %s\n", parameters->name);
}
if (ignore_missing) {
return false;
}
printf("DISK %s - ", _("CRITICAL"));
die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"),
strerror(errno));
}
return true;
}
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;
if (freespace_ignore_reserved) {
/* option activated : we subtract the root-reserved space from the total */
total = fsp.fsu_blocks - available_to_root + available;
} else {
/* default behaviour : take all the blocks into account */
total = fsp.fsu_blocks;
}
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;
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;
} else {
/* default behaviour : take all the inodes into account */
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((unsigned long long)measurement_unit.total_bytes, false));
}
// Free space just internally for computation
mp_perfdata free_space_pd = perfdata_init();
free_space_pd.label = measurement_unit.name;
free_space_pd.value = mp_create_pd_value(measurement_unit.free_bytes);
free_space_pd =
mp_pd_set_thresholds(free_space_pd, measurement_unit.freespace_bytes_thresholds);
freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(free_space_pd));
// Used space for display
mp_perfdata used_space_pd = perfdata_init();
used_space_pd.label = measurement_unit.name;
used_space_pd.value = mp_create_pd_value(measurement_unit.used_bytes);
used_space_pd =
mp_set_pd_max_value(used_space_pd, mp_create_pd_value(measurement_unit.total_bytes));
used_space_pd = mp_set_pd_min_value(used_space_pd, mp_create_pd_value(0));
used_space_pd.uom = "B";
// 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);
free_space_pd =
mp_pd_set_thresholds(free_space_pd, 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);
free_space_pd =
mp_pd_set_thresholds(free_space_pd, measurement_unit.freespace_bytes_thresholds);
}
mp_thresholds thr_used_space = measurement_unit.freespace_bytes_thresholds;
if (thr_used_space.critical_is_set) {
thr_used_space.critical.alert_on_inside_range =
!thr_used_space.critical.alert_on_inside_range;
}
if (thr_used_space.warning_is_set) {
thr_used_space.warning.alert_on_inside_range =
!thr_used_space.warning.alert_on_inside_range;
}
used_space_pd = mp_pd_set_thresholds(used_space_pd, thr_used_space);
mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space_pd);
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);
mp_perfdata inode_percentage_pd = perfdata_init();
inode_percentage_pd = mp_set_pd_value(inode_percentage_pd, free_inode_percentage);
inode_percentage_pd = mp_pd_set_thresholds(inode_percentage_pd,
measurement_unit.freeinodes_percent_thresholds);
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(inode_percentage_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;
}