summaryrefslogtreecommitdiffstats
path: root/plugins/check_disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_disk.c')
-rw-r--r--plugins/check_disk.c1447
1 files changed, 819 insertions, 628 deletions
diff --git a/plugins/check_disk.c b/plugins/check_disk.c
index 037a6f7a..d42b5486 100644
--- a/plugins/check_disk.c
+++ b/plugins/check_disk.c
@@ -31,24 +31,35 @@ const char *program_name = "check_disk"; /* Required for coreutils libs */
31const char *copyright = "1999-2024"; 31const char *copyright = "1999-2024";
32const char *email = "devel@monitoring-plugins.org"; 32const char *email = "devel@monitoring-plugins.org";
33 33
34#include "states.h"
34#include "common.h" 35#include "common.h"
36#include "output.h"
37#include "perfdata.h"
38#include "utils_base.h"
39#include "lib/thresholds.h"
40
35#ifdef HAVE_SYS_STAT_H 41#ifdef HAVE_SYS_STAT_H
36# include <sys/stat.h> 42# include <sys/stat.h>
37#endif 43#endif
44
38#if HAVE_INTTYPES_H 45#if HAVE_INTTYPES_H
39# include <inttypes.h> 46# include <inttypes.h>
40#endif 47#endif
48
41#include <assert.h> 49#include <assert.h>
42#include "popen.h"
43#include "utils.h"
44#include "utils_disk.h"
45#include <stdarg.h> 50#include <stdarg.h>
46#include "fsusage.h" 51#include <stdint.h>
47#include "mountlist.h"
48#include <float.h> 52#include <float.h>
53#include "./popen.h"
54#include "./utils.h"
55#include "../gl/fsusage.h"
56#include "../gl/mountlist.h"
57#include "./check_disk.d/utils_disk.h"
58
49#if HAVE_LIMITS_H 59#if HAVE_LIMITS_H
50# include <limits.h> 60# include <limits.h>
51#endif 61#endif
62
52#include "regex.h" 63#include "regex.h"
53 64
54#ifdef __CYGWIN__ 65#ifdef __CYGWIN__
@@ -57,424 +68,322 @@ const char *email = "devel@monitoring-plugins.org";
57# define ERROR -1 68# define ERROR -1
58#endif 69#endif
59 70
60/* If nonzero, show even filesystems with zero size or
61 uninteresting types. */
62static int show_all_fs = 1;
63
64/* If nonzero, show only local filesystems. */
65static int show_local_fs = 0;
66
67/* If nonzero, show only local filesystems but call stat() on remote ones. */
68static int stat_remote_fs = 0;
69
70/* If positive, the units to use when printing sizes;
71 if negative, the human-readable base. */
72/* static int output_block_size; */
73
74/* If nonzero, invoke the `sync' system call before getting any usage data.
75 Using this option can make df very slow, especially with many or very
76 busy disks. Note that this may make a difference on some systems --
77 SunOs4.1.3, for one. It is *not* necessary on Linux. */
78/* static int require_sync = 0; */
79
80/* Linked list of filesystem types to display.
81 If `fs_select_list' is NULL, list all types.
82 This table is generated dynamically from command-line options,
83 rather than hardcoding into the program what it thinks are the
84 valid filesystem types; let the user specify any filesystem type
85 they want to, and if there are any filesystems of that type, they
86 will be shown.
87
88 Some filesystem types:
89 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
90
91/* static struct parameter_list *fs_select_list; */
92
93/* Linked list of filesystem types to omit.
94 If the list is empty, don't exclude any types. */
95static struct regex_list *fs_exclude_list = NULL;
96
97/* Linked list of filesystem types to check.
98 If the list is empty, include all types. */
99static struct regex_list *fs_include_list;
100
101static struct name_list *dp_exclude_list;
102
103static struct parameter_list *path_select_list = NULL;
104
105/* Linked list of mounted filesystems. */
106static struct mount_entry *mount_list;
107
108/* For long options that have no equivalent short option, use a
109 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
110enum {
111 SYNC_OPTION = CHAR_MAX + 1,
112 NO_SYNC_OPTION,
113 BLOCK_SIZE_OPTION
114};
115
116#ifdef _AIX 71#ifdef _AIX
117# pragma alloca 72# pragma alloca
118#endif 73#endif
119 74
120static int process_arguments(int /*argc*/, char ** /*argv*/); 75typedef struct {
121static void set_all_thresholds(struct parameter_list *path); 76 int errorcode;
122static void print_help(void); 77 check_disk_config config;
78} check_disk_config_wrapper;
79static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
80
81static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
82 char *crit_freespace_units, char *warn_freespace_percent,
83 char *crit_freespace_percent, char *warn_freeinodes_percent,
84 char *crit_freeinodes_percent);
85static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/);
86static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/);
87
88/*
89 * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control
90 * how reserved and inodes should be judged (ignored or not)
91 */
92static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp,
93 bool freespace_ignore_reserved);
94static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit,
95 bool display_inodes_perfdata, byte_unit unit);
96
123void print_usage(void); 97void print_usage(void);
124static double calculate_percent(uintmax_t, uintmax_t); 98static void print_help(void);
125static bool stat_path(struct parameter_list *p);
126static void get_stats(struct parameter_list *p, struct fs_usage *fsp);
127static void get_path_stats(struct parameter_list *p, struct fs_usage *fsp);
128 99
129static char *units;
130static uintmax_t mult = 1024 * 1024;
131static int verbose = 0; 100static int verbose = 0;
132static bool erronly = false;
133static bool display_mntp = false;
134static bool exact_match = false;
135static bool ignore_missing = false;
136static bool freespace_ignore_reserved = false;
137static bool display_inodes_perfdata = false;
138static char *warn_freespace_units = NULL;
139static char *crit_freespace_units = NULL;
140static char *warn_freespace_percent = NULL;
141static char *crit_freespace_percent = NULL;
142static char *warn_usedspace_units = NULL;
143static char *crit_usedspace_units = NULL;
144static char *warn_usedspace_percent = NULL;
145static char *crit_usedspace_percent = NULL;
146static char *warn_usedinodes_percent = NULL;
147static char *crit_usedinodes_percent = NULL;
148static char *warn_freeinodes_percent = NULL;
149static char *crit_freeinodes_percent = NULL;
150static bool path_selected = false;
151static bool path_ignored = false;
152static char *group = NULL;
153static struct stat *stat_buf;
154static struct name_list *seen = NULL;
155 101
156int main(int argc, char **argv) { 102// This would not be necessary in C23!!
157 int result = STATE_UNKNOWN; 103const byte_unit Bytes_Factor = 1;
158 int disk_result = STATE_UNKNOWN; 104const byte_unit KibiBytes_factor = 1024;
159 char *output = NULL; 105const byte_unit MebiBytes_factor = 1048576;
160 char *ignored = NULL; 106const byte_unit GibiBytes_factor = 1073741824;
161 char *details = NULL; 107const byte_unit TebiBytes_factor = 1099511627776;
162 char *perf = NULL; 108const byte_unit PebiBytes_factor = 1125899906842624;
163 char *perf_ilabel = NULL; 109const byte_unit ExbiBytes_factor = 1152921504606846976;
164 char *preamble = " - free space:"; 110const byte_unit KiloBytes_factor = 1000;
165 char *ignored_preamble = " - ignored paths:"; 111const byte_unit MegaBytes_factor = 1000000;
166 char *flag_header = NULL; 112const byte_unit GigaBytes_factor = 1000000000;
167 int temp_result = STATE_UNKNOWN; 113const byte_unit TeraBytes_factor = 1000000000000;
168 114const byte_unit PetaBytes_factor = 1000000000000000;
169 struct mount_entry *me = NULL; 115const byte_unit ExaBytes_factor = 1000000000000000000;
170 struct fs_usage fsp = {0};
171 struct parameter_list *temp_list = NULL;
172 struct parameter_list *path = NULL;
173
174#ifdef __CYGWIN__
175 char mountdir[32];
176#endif
177
178 output = strdup("");
179 ignored = strdup("");
180 details = strdup("");
181 perf = strdup("");
182 perf_ilabel = strdup("");
183 stat_buf = malloc(sizeof *stat_buf);
184 116
117int main(int argc, char **argv) {
185 setlocale(LC_ALL, ""); 118 setlocale(LC_ALL, "");
186 bindtextdomain(PACKAGE, LOCALEDIR); 119 bindtextdomain(PACKAGE, LOCALEDIR);
187 textdomain(PACKAGE); 120 textdomain(PACKAGE);
188 121
189 mount_list = read_file_system_list(0); 122#ifdef __CYGWIN__
123 char mountdir[32];
124#endif
190 125
191 /* Parse extra opts if any */ 126 // Parse extra opts if any
192 argv = np_extra_opts(&argc, argv, progname); 127 argv = np_extra_opts(&argc, argv, progname);
193 128
194 if (process_arguments(argc, argv) == ERROR) 129 check_disk_config_wrapper tmp_config = process_arguments(argc, argv);
130 if (tmp_config.errorcode == ERROR) {
195 usage4(_("Could not parse arguments")); 131 usage4(_("Could not parse arguments"));
132 }
196 133
197 /* If a list of paths has not been selected, find entire 134 check_disk_config config = tmp_config.config;
198 mount list and create list of paths 135
199 */ 136 if (config.output_format_is_set) {
200 if (path_selected == false && path_ignored == false) { 137 mp_set_format(config.output_format);
201 for (me = mount_list; me; me = me->me_next) {
202 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) {
203 path = np_add_parameter(&path_select_list, me->me_mountdir);
204 }
205 path->best_match = me;
206 path->group = group;
207 set_all_thresholds(path);
208 }
209 } 138 }
210 139
211 if (path_ignored == false) { 140 if (config.erronly) {
212 np_set_best_match(path_select_list, mount_list, exact_match); 141 mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY);
213 } 142 }
214 143
215 /* Error if no match found for specified paths */ 144 if (!config.path_ignored) {
216 temp_list = path_select_list; 145 mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list,
146 config.exact_match);
147 }
217 148
218 while (path_select_list) { 149 // Error if no match found for specified paths
219 if (!path_select_list->best_match && ignore_missing == true) { 150 for (parameter_list_elem *elem = config.path_select_list.first; elem;) {
220 /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ 151 if (!elem->best_match && config.ignore_missing) {
221 if (path_select_list == temp_list) {
222 temp_list = path_select_list->name_next;
223 }
224 /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */
225 xasprintf(&ignored, "%s %s;", ignored, path_select_list->name);
226 /* Delete the path from the list so that it is not stat-checked later in the code. */ 152 /* Delete the path from the list so that it is not stat-checked later in the code. */
227 path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev); 153 elem = mp_int_fs_list_del(&config.path_select_list, elem);
228 } else if (!path_select_list->best_match) { 154 continue;
155 }
156 if (!elem->best_match) {
229 /* Without --ignore-missing option, exit with Critical state. */ 157 /* Without --ignore-missing option, exit with Critical state. */
230 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name); 158 die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name);
231 } else {
232 /* Continue jumping through the list */
233 path_select_list = path_select_list->name_next;
234 } 159 }
235 }
236 160
237 path_select_list = temp_list; 161 elem = mp_int_fs_list_get_next(elem);
162 }
238 163
239 if (!path_select_list && ignore_missing == true) { 164 mp_check overall = mp_check_init();
240 result = STATE_OK; 165 if (config.path_select_list.length == 0) {
241 if (verbose >= 2) { 166 mp_subcheck none_sc = mp_subcheck_init();
242 printf("None of the provided paths were found\n"); 167 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
168 if (config.ignore_missing) {
169 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
170 } else {
171 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
172 if (verbose >= 2) {
173 printf("None of the provided paths were found\n");
174 }
243 } 175 }
176 mp_add_subcheck_to_check(&overall, none_sc);
177 mp_exit(overall);
244 } 178 }
245 179
246 /* Process for every path in list */ 180 // Filter list first
247 for (path = path_select_list; path; path = path->name_next) { 181 for (parameter_list_elem *path = config.path_select_list.first; path;) {
248 if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) 182 if (!path->best_match) {
249 printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, 183 path = mp_int_fs_list_del(&config.path_select_list, path);
250 path->freespace_percent->critical->end);
251
252 if (verbose >= 3 && path->group != NULL)
253 printf("Group of %s: %s\n", path->name, path->group);
254
255 /* reset disk result */
256 disk_result = STATE_UNKNOWN;
257
258 me = path->best_match;
259
260 if (!me) {
261 continue; 184 continue;
262 } 185 }
263 186
187 struct mount_entry *mount_entry = path->best_match;
188
264#ifdef __CYGWIN__ 189#ifdef __CYGWIN__
265 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) 190 if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) {
191 path = mp_int_fs_list_del(&config.path_select_list, path);
266 continue; 192 continue;
193 }
194
195 char *mountdir = NULL;
267 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); 196 snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10);
268 if (GetDriveType(mountdir) != DRIVE_FIXED) 197 if (GetDriveType(mountdir) != DRIVE_FIXED) {
269 me->me_remote = 1; 198 mount_entry->me_remote = 1;
199 }
270#endif 200#endif
271 /* Filters */
272 201
273 /* Remove filesystems already seen */ 202 /* Remove filesystems already seen */
274 if (np_seen_name(seen, me->me_mountdir)) { 203 if (np_seen_name(config.seen, mount_entry->me_mountdir)) {
204 path = mp_int_fs_list_del(&config.path_select_list, path);
275 continue; 205 continue;
276 } 206 }
277 np_add_name(&seen, me->me_mountdir);
278 207
279 if (path->group == NULL) { 208 if (path->group == NULL) {
280 /* Skip remote filesystems if we're not interested in them */ 209 if (config.fs_exclude_list &&
281 if (me->me_remote && show_local_fs) { 210 np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) {
282 if (stat_remote_fs) { 211 // Skip excluded fs's
283 if (!stat_path(path) && ignore_missing == true) { 212 path = mp_int_fs_list_del(&config.path_select_list, path);
284 result = STATE_OK;
285 xasprintf(&ignored, "%s %s;", ignored, path->name);
286 }
287 }
288 continue; 213 continue;
289 /* Skip pseudo fs's if we haven't asked for all fs's */
290 } 214 }
291 if (me->me_dummy && !show_all_fs) { 215
292 continue; 216 if (config.device_path_exclude_list &&
293 /* Skip excluded fstypes */ 217 (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) ||
294 } 218 np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) {
295 if (fs_exclude_list && np_find_regmatch(fs_exclude_list, me->me_type)) { 219 // Skip excluded device or mount paths
220 path = mp_int_fs_list_del(&config.path_select_list, path);
296 continue; 221 continue;
297 /* Skip excluded fs's */
298 } 222 }
299 if (dp_exclude_list && (np_find_name(dp_exclude_list, me->me_devname) || np_find_name(dp_exclude_list, me->me_mountdir))) { 223
224 if (config.fs_include_list &&
225 !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) {
226 // Skip not included fstypes
227 path = mp_int_fs_list_del(&config.path_select_list, path);
300 continue; 228 continue;
301 /* Skip not included fstypes */
302 } 229 }
303 if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) { 230
231 /* Skip remote filesystems if we're not interested in them */
232 if (mount_entry->me_remote && config.show_local_fs) {
233 if (config.stat_remote_fs) {
234 // TODO Stat here
235 if (!stat_path(path, config.ignore_missing) && config.ignore_missing) {
236 }
237 }
304 continue; 238 continue;
305 } 239 }
306 }
307 240
308 if (!stat_path(path)) { 241 // TODO why stat here? remove unstatable fs?
309 if (ignore_missing == true) { 242 if (!stat_path(path, config.ignore_missing)) {
310 result = STATE_OK; 243 // if (config.ignore_missing) {
311 xasprintf(&ignored, "%s %s;", ignored, path->name); 244 // xasprintf(&ignored, "%s %s;", ignored, path->name);
245 // }
246 // not accessible, remove from list
247 path = mp_int_fs_list_del(&config.path_select_list, path);
248 continue;
312 } 249 }
313 continue;
314 } 250 }
315 get_fs_usage(me->me_mountdir, me->me_devname, &fsp);
316
317 if (fsp.fsu_blocks && strcmp("none", me->me_mountdir)) {
318 get_stats(path, &fsp);
319
320 if (verbose >= 3) {
321 printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f "
322 "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n",
323 me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units,
324 path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult);
325 }
326
327 /* Threshold comparisons */
328
329 temp_result = get_status(path->dfree_units, path->freespace_units);
330 if (verbose >= 3)
331 printf("Freespace_units result=%d\n", temp_result);
332 disk_result = max_state(disk_result, temp_result);
333
334 temp_result = get_status(path->dfree_pct, path->freespace_percent);
335 if (verbose >= 3)
336 printf("Freespace%% result=%d\n", temp_result);
337 disk_result = max_state(disk_result, temp_result);
338 251
339 temp_result = get_status(path->dused_units, path->usedspace_units); 252 path = mp_int_fs_list_get_next(path);
340 if (verbose >= 3) 253 }
341 printf("Usedspace_units result=%d\n", temp_result);
342 disk_result = max_state(disk_result, temp_result);
343
344 temp_result = get_status(path->dused_pct, path->usedspace_percent);
345 if (verbose >= 3)
346 printf("Usedspace_percent result=%d\n", temp_result);
347 disk_result = max_state(disk_result, temp_result);
348
349 temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent);
350 if (verbose >= 3)
351 printf("Usedinodes_percent result=%d\n", temp_result);
352 disk_result = max_state(disk_result, temp_result);
353
354 temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent);
355 if (verbose >= 3)
356 printf("Freeinodes_percent result=%d\n", temp_result);
357 disk_result = max_state(disk_result, temp_result);
358
359 result = max_state(result, disk_result);
360
361 /* What a mess of units. The output shows free space, the perf data shows used space. Yikes!
362 Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf
363 data. Assumption that start=0. Roll on new syntax...
364 */
365
366 /* *_high_tide must be reinitialized at each run */
367 uint64_t warning_high_tide = UINT64_MAX;
368 254
369 if (path->freespace_units->warning != NULL) { 255 // now get the actual measurements
370 warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult; 256 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) {
371 } 257 // Get actual metrics here
372 if (path->freespace_percent->warning != NULL) { 258 struct mount_entry *mount_entry = filesystem->best_match;
373 warning_high_tide = 259 struct fs_usage fsp = {0};
374 min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * (path->dtotal_units * mult))); 260 get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp);
375 }
376 261
377 uint64_t critical_high_tide = UINT64_MAX; 262 if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) {
263 *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved);
378 264
379 if (path->freespace_units->critical != NULL) { 265 if (verbose >= 3) {
380 critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult; 266 printf("For %s, used_units=%lu free_units=%lu total_units=%lu "
381 } 267 "fsp.fsu_blocksize=%lu\n",
382 if (path->freespace_percent->critical != NULL) { 268 mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes,
383 critical_high_tide = 269 filesystem->total_bytes, fsp.fsu_blocksize);
384 min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * (path->dtotal_units * mult)));
385 } 270 }
271 } else {
272 // failed to retrieve file system data or not mounted?
273 filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem);
274 continue;
275 }
276 filesystem = mp_int_fs_list_get_next(filesystem);
277 }
386 278
387 /* Nb: *_high_tide are unset when == UINT64_MAX */ 279 if (verbose > 2) {
388 xasprintf(&perf, "%s %s", perf, 280 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
389 perfdata_uint64((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, 281 filesystem = mp_int_fs_list_get_next(filesystem)) {
390 path->dused_units * mult, "B", (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide, 282 assert(filesystem->best_match != NULL);
391 (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide, true, 0, true, 283 if (filesystem->best_match == NULL) {
392 path->dtotal_units * mult)); 284 printf("Filesystem path %s has no mount_entry!\n", filesystem->name);
393 285 } else {
394 if (display_inodes_perfdata) { 286 // printf("Filesystem path %s has a mount_entry!\n", filesystem->name);
395 /* *_high_tide must be reinitialized at each run */
396 warning_high_tide = UINT64_MAX;
397 critical_high_tide = UINT64_MAX;
398
399 if (path->freeinodes_percent->warning != NULL) {
400 warning_high_tide = (uint64_t)fabs(
401 min((double)warning_high_tide, (double)(1.0 - path->freeinodes_percent->warning->end / 100) * path->inodes_total));
402 }
403 if (path->freeinodes_percent->critical != NULL) {
404 critical_high_tide = (uint64_t)fabs(min(
405 (double)critical_high_tide, (double)(1.0 - path->freeinodes_percent->critical->end / 100) * path->inodes_total));
406 }
407
408 xasprintf(&perf_ilabel, "%s (inodes)",
409 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
410 /* Nb: *_high_tide are unset when == UINT64_MAX */
411 xasprintf(&perf, "%s %s", perf,
412 perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX ? true : false),
413 warning_high_tide, (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide, true, 0,
414 true, path->inodes_total));
415 } 287 }
288 }
289 }
416 290
417 if (disk_result == STATE_OK && erronly && !verbose) 291 measurement_unit_list *measurements = NULL;
418 continue; 292 measurement_unit_list *current = NULL;
419 293 // create measuring units, because of groups
420 if (disk_result && verbose >= 1) { 294 for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;
421 xasprintf(&flag_header, " %s [", state_text(disk_result)); 295 filesystem = mp_int_fs_list_get_next(filesystem)) {
296 assert(filesystem->best_match != NULL);
297
298 if (filesystem->group == NULL) {
299 // create a measurement unit for the fs
300 measurement_unit unit =
301 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
302 if (measurements == NULL) {
303 measurements = current = add_measurement_list(NULL, unit);
422 } else { 304 } else {
423 xasprintf(&flag_header, ""); 305 current = add_measurement_list(measurements, unit);
424 } 306 }
425 xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, 307 } else {
426 (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, path->dfree_units, units, 308 // Grouped elements are consecutive
427 path->dfree_pct); 309 if (measurements == NULL) {
428 if (path->dused_inodes_percent < 0) { 310 // first entry
429 xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : "")); 311 measurement_unit unit =
312 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
313 unit.name = strdup(filesystem->group);
314 measurements = current = add_measurement_list(NULL, unit);
430 } else { 315 } else {
431 xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : "")); 316 // if this is the first element of a group, the name of the previous entry is
317 // different
318 if (strcmp(filesystem->group, current->unit.name) != 0) {
319 // so, this must be the first element of a group
320 measurement_unit unit =
321 create_measurement_unit_from_filesystem(*filesystem, config.display_mntp);
322 unit.name = filesystem->group;
323 current = add_measurement_list(measurements, unit);
324
325 } else {
326 // NOT the first entry of a group, add info to the other one
327 current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem);
328 }
432 } 329 }
433 free(flag_header);
434 } 330 }
435 } 331 }
436 332
437 if (verbose >= 2) 333 /* Process for every path in list */
438 xasprintf(&output, "%s%s", output, details); 334 if (measurements != NULL) {
335 for (measurement_unit_list *unit = measurements; unit; unit = unit->next) {
336 mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata,
337 config.display_unit);
338 mp_add_subcheck_to_check(&overall, unit_sc);
339 }
340 } else {
341 // Apparently no machting fs found
342 mp_subcheck none_sc = mp_subcheck_init();
343 xasprintf(&none_sc.output, "No filesystems were found for the provided parameters");
439 344
440 if (strcmp(output, "") == 0 && !erronly) { 345 if (config.ignore_missing) {
441 preamble = ""; 346 none_sc = mp_set_subcheck_state(none_sc, STATE_OK);
442 xasprintf(&output, " - No disks were found for provided parameters"); 347 } else {
348 none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN);
349 }
350 mp_add_subcheck_to_check(&overall, none_sc);
443 } 351 }
444 352
445 printf("DISK %s%s%s%s%s|%s\n", state_text(result), ((erronly && result == STATE_OK)) ? "" : preamble, output, 353 mp_exit(overall);
446 (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf);
447 return result;
448} 354}
449 355
450double calculate_percent(uintmax_t value, uintmax_t total) { 356double calculate_percent(uintmax_t value, uintmax_t total) {
451 double pct = -1; 357 double pct = -1;
452 if (value <= DBL_MAX && total != 0) { 358 if (value <= DBL_MAX && total != 0) {
453 pct = (double)value / total * 100.0; 359 pct = (double)value / (double)total * 100.0;
454 } 360 }
361
455 return pct; 362 return pct;
456} 363}
457 364
458/* process command-line arguments */ 365/* process command-line arguments */
459int process_arguments(int argc, char **argv) { 366check_disk_config_wrapper process_arguments(int argc, char **argv) {
460 int c; 367
461 int err; 368 check_disk_config_wrapper result = {
462 struct parameter_list *se; 369 .errorcode = OK,
463 struct parameter_list *temp_list = NULL; 370 .config = check_disk_config_init(),
464 struct parameter_list *previous = NULL; 371 };
465 struct mount_entry *me; 372
466 regex_t re; 373 if (argc < 2) {
467 int cflags = REG_NOSUB | REG_EXTENDED; 374 result.errorcode = ERROR;
468 int default_cflags = cflags; 375 return result;
469 char errbuf[MAX_INPUT_BUFFER]; 376 }
470 int fnd = 0; 377
378 enum {
379 output_format_index = CHAR_MAX + 1,
380 display_unit_index,
381 };
471 382
472 int option = 0;
473 static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, 383 static struct option longopts[] = {{"timeout", required_argument, 0, 't'},
474 {"warning", required_argument, 0, 'w'}, 384 {"warning", required_argument, 0, 'w'},
475 {"critical", required_argument, 0, 'c'}, 385 {"critical", required_argument, 0, 'c'},
476 {"iwarning", required_argument, 0, 'W'}, 386 {"iwarning", required_argument, 0, 'W'},
477 /* Dang, -C is taken. We might want to reshuffle this. */
478 {"icritical", required_argument, 0, 'K'}, 387 {"icritical", required_argument, 0, 'K'},
479 {"kilobytes", no_argument, 0, 'k'}, 388 {"kilobytes", no_argument, 0, 'k'},
480 {"megabytes", no_argument, 0, 'm'}, 389 {"megabytes", no_argument, 0, 'm'},
@@ -507,24 +416,43 @@ int process_arguments(int argc, char **argv) {
507 {"clear", no_argument, 0, 'C'}, 416 {"clear", no_argument, 0, 'C'},
508 {"version", no_argument, 0, 'V'}, 417 {"version", no_argument, 0, 'V'},
509 {"help", no_argument, 0, 'h'}, 418 {"help", no_argument, 0, 'h'},
419 {"output-format", required_argument, 0, output_format_index},
420 {"display-unit", required_argument, 0, display_unit_index},
510 {0, 0, 0, 0}}; 421 {0, 0, 0, 0}};
511 422
512 if (argc < 2) 423 for (int index = 1; index < argc; index++) {
513 return ERROR; 424 if (strcmp("-to", argv[index]) == 0) {
425 strcpy(argv[index], "-t");
426 }
427 }
428
429 int cflags = REG_NOSUB | REG_EXTENDED;
430 int default_cflags = cflags;
431 char *warn_freespace_units = NULL;
432 char *crit_freespace_units = NULL;
433 char *warn_freespace_percent = NULL;
434 char *crit_freespace_percent = NULL;
435 char *warn_freeinodes_percent = NULL;
436 char *crit_freeinodes_percent = NULL;
514 437
515 np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); 438 bool path_selected = false;
439 char *group = NULL;
440 byte_unit unit = MebiBytes_factor;
516 441
517 for (c = 1; c < argc; c++) 442 result.config.mount_list = read_file_system_list(false);
518 if (strcmp("-to", argv[c]) == 0)
519 strcpy(argv[c], "-t");
520 443
521 while (1) { 444 np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED);
522 c = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
523 445
524 if (c == -1 || c == EOF) 446 while (true) {
447 int option = 0;
448 int option_index = getopt_long(
449 argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option);
450
451 if (option_index == -1 || option_index == EOF) {
525 break; 452 break;
453 }
526 454
527 switch (c) { 455 switch (option_index) {
528 case 't': /* timeout period */ 456 case 't': /* timeout period */
529 if (is_integer(optarg)) { 457 if (is_integer(optarg)) {
530 timeout_interval = atoi(optarg); 458 timeout_interval = atoi(optarg);
@@ -555,10 +483,10 @@ int process_arguments(int argc, char **argv) {
555 break; 483 break;
556 484
557 /* Awful mistake where the range values do not make sense. Normally, 485 /* Awful mistake where the range values do not make sense. Normally,
558 you alert if the value is within the range, but since we are using 486 * you alert if the value is within the range, but since we are using
559 freespace, we have to alert if outside the range. Thus we artificially 487 * freespace, we have to alert if outside the range. Thus we artificially
560 force @ at the beginning of the range, so that it is backwards compatible 488 * force @ at the beginning of the range, so that it is backwards compatible
561 */ 489 */
562 case 'c': /* critical threshold */ 490 case 'c': /* critical threshold */
563 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { 491 if (!is_percentage_expression(optarg) && !is_numeric(optarg)) {
564 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); 492 die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg);
@@ -594,181 +522,193 @@ int process_arguments(int argc, char **argv) {
594 } 522 }
595 break; 523 break;
596 case 'u': 524 case 'u':
597 if (units)
598 free(units);
599 if (!strcasecmp(optarg, "bytes")) { 525 if (!strcasecmp(optarg, "bytes")) {
600 mult = (uintmax_t)1; 526 unit = Bytes_Factor;
601 units = strdup("B");
602 } else if (!strcmp(optarg, "KiB")) { 527 } else if (!strcmp(optarg, "KiB")) {
603 mult = (uintmax_t)1024; 528 unit = KibiBytes_factor;
604 units = strdup("KiB");
605 } else if (!strcmp(optarg, "kB")) { 529 } else if (!strcmp(optarg, "kB")) {
606 mult = (uintmax_t)1000; 530 unit = KiloBytes_factor;
607 units = strdup("kB");
608 } else if (!strcmp(optarg, "MiB")) { 531 } else if (!strcmp(optarg, "MiB")) {
609 mult = (uintmax_t)1024 * 1024; 532 unit = MebiBytes_factor;
610 units = strdup("MiB");
611 } else if (!strcmp(optarg, "MB")) { 533 } else if (!strcmp(optarg, "MB")) {
612 mult = (uintmax_t)1000 * 1000; 534 unit = MegaBytes_factor;
613 units = strdup("MB");
614 } else if (!strcmp(optarg, "GiB")) { 535 } else if (!strcmp(optarg, "GiB")) {
615 mult = (uintmax_t)1024 * 1024 * 1024; 536 unit = GibiBytes_factor;
616 units = strdup("GiB");
617 } else if (!strcmp(optarg, "GB")) { 537 } else if (!strcmp(optarg, "GB")) {
618 mult = (uintmax_t)1000 * 1000 * 1000; 538 unit = GigaBytes_factor;
619 units = strdup("GB");
620 } else if (!strcmp(optarg, "TiB")) { 539 } else if (!strcmp(optarg, "TiB")) {
621 mult = (uintmax_t)1024 * 1024 * 1024 * 1024; 540 unit = TebiBytes_factor;
622 units = strdup("TiB");
623 } else if (!strcmp(optarg, "TB")) { 541 } else if (!strcmp(optarg, "TB")) {
624 mult = (uintmax_t)1000 * 1000 * 1000 * 1000; 542 unit = TeraBytes_factor;
625 units = strdup("TB");
626 } else if (!strcmp(optarg, "PiB")) { 543 } else if (!strcmp(optarg, "PiB")) {
627 mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; 544 unit = PebiBytes_factor;
628 units = strdup("PiB");
629 } else if (!strcmp(optarg, "PB")) { 545 } else if (!strcmp(optarg, "PB")) {
630 mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; 546 unit = PetaBytes_factor;
631 units = strdup("PB");
632 } else { 547 } else {
633 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); 548 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
634 } 549 }
635 if (units == NULL)
636 die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units");
637 break; 550 break;
638 case 'k': /* display mountpoint */ 551 case 'k':
639 mult = 1024; 552 unit = KibiBytes_factor;
640 if (units)
641 free(units);
642 units = strdup("kiB");
643 break; 553 break;
644 case 'm': /* display mountpoint */ 554 case 'm':
645 mult = 1024 * 1024; 555 unit = MebiBytes_factor;
646 if (units) 556 break;
647 free(units); 557 case display_unit_index:
648 units = strdup("MiB"); 558 if (!strcasecmp(optarg, "bytes")) {
559 result.config.display_unit = Bytes;
560 } else if (!strcmp(optarg, "KiB")) {
561 result.config.display_unit = KibiBytes;
562 } else if (!strcmp(optarg, "kB")) {
563 result.config.display_unit = KiloBytes;
564 } else if (!strcmp(optarg, "MiB")) {
565 result.config.display_unit = MebiBytes;
566 } else if (!strcmp(optarg, "MB")) {
567 result.config.display_unit = MegaBytes;
568 } else if (!strcmp(optarg, "GiB")) {
569 result.config.display_unit = GibiBytes;
570 } else if (!strcmp(optarg, "GB")) {
571 result.config.display_unit = GigaBytes;
572 } else if (!strcmp(optarg, "TiB")) {
573 result.config.display_unit = TebiBytes;
574 } else if (!strcmp(optarg, "TB")) {
575 result.config.display_unit = TeraBytes;
576 } else if (!strcmp(optarg, "PiB")) {
577 result.config.display_unit = PebiBytes;
578 } else if (!strcmp(optarg, "PB")) {
579 result.config.display_unit = PetaBytes;
580 } else {
581 die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg);
582 }
649 break; 583 break;
650 case 'L': 584 case 'L':
651 stat_remote_fs = 1; 585 result.config.stat_remote_fs = true;
652 /* fallthrough */ 586 /* fallthrough */
653 case 'l': 587 case 'l':
654 show_local_fs = 1; 588 result.config.show_local_fs = true;
655 break; 589 break;
656 case 'P': 590 case 'P':
657 display_inodes_perfdata = 1; 591 result.config.display_inodes_perfdata = true;
658 break; 592 break;
659 case 'p': /* select path */ 593 case 'p': /* select path */ {
660 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 594 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
661 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 595 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
662 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { 596 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
663 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); 597 _("Must set a threshold value before using -p\n"));
664 } 598 }
665 599
666 /* add parameter if not found. overwrite thresholds if path has already been added */ 600 /* add parameter if not found. overwrite thresholds if path has already been added */
667 if (!(se = np_find_parameter(path_select_list, optarg))) { 601 parameter_list_elem *search_entry;
668 se = np_add_parameter(&path_select_list, optarg); 602 if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) {
669 603 search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg);
670 if (stat(optarg, &stat_buf[0]) && ignore_missing == true) { 604
671 path_ignored = true; 605 // struct stat stat_buf = {};
672 break; 606 // if (stat(optarg, &stat_buf) && result.config.ignore_missing) {
673 } 607 // result.config.path_ignored = true;
608 // break;
609 // }
674 } 610 }
675 se->group = group; 611 search_entry->group = group;
676 set_all_thresholds(se); 612 set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units,
613 warn_freespace_percent, crit_freespace_percent,
614
615 warn_freeinodes_percent, crit_freeinodes_percent);
677 616
678 /* With autofs, it is required to stat() the path before re-populating the mount_list */ 617 /* With autofs, it is required to stat() the path before re-populating the mount_list */
679 if (!stat_path(se)) { 618 // if (!stat_path(se, result.config.ignore_missing)) {
680 break; 619 // break;
681 } 620 // }
682 /* NB: We can't free the old mount_list "just like that": both list pointers and struct 621 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
683 * pointers are copied around. One of the reason it wasn't done yet is that other parts 622 result.config.exact_match);
684 * of check_disk need the same kind of cleanup so it'd better be done as a whole */
685 mount_list = read_file_system_list(0);
686 np_set_best_match(se, mount_list, exact_match);
687 623
688 path_selected = true; 624 path_selected = true;
689 break; 625 } break;
690 case 'x': /* exclude path or partition */ 626 case 'x': /* exclude path or partition */
691 np_add_name(&dp_exclude_list, optarg); 627 np_add_name(&result.config.device_path_exclude_list, optarg);
692 break; 628 break;
693 case 'X': /* exclude file system type */ 629 case 'X': /* exclude file system type */ {
694 err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); 630 int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED);
695 if (err != 0) { 631 if (err != 0) {
696 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 632 char errbuf[MAX_INPUT_BUFFER];
697 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 633 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
634 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
635 _("Could not compile regular expression"), errbuf);
698 } 636 }
699 break; 637 break;
700 case 'N': /* include file system type */ 638 case 'N': /* include file system type */
701 err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED); 639 err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED);
702 if (err != 0) { 640 if (err != 0) {
703 regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); 641 char errbuf[MAX_INPUT_BUFFER];
704 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 642 regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER);
643 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
644 _("Could not compile regular expression"), errbuf);
705 } 645 }
706 break; 646 } break;
707 case 'v': /* verbose */ 647 case 'v': /* verbose */
708 verbose++; 648 verbose++;
709 break; 649 break;
710 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ 650 case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */
711 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ 651 /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */
712 erronly = true; 652 result.config.erronly = true;
713 break; 653 break;
714 case 'e': 654 case 'e':
715 erronly = true; 655 result.config.erronly = true;
716 break; 656 break;
717 case 'E': 657 case 'E':
718 if (path_selected) 658 if (path_selected) {
719 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); 659 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
720 exact_match = true; 660 _("Must set -E before selecting paths\n"));
661 }
662 result.config.exact_match = true;
721 break; 663 break;
722 case 'f': 664 case 'f':
723 freespace_ignore_reserved = true; 665 result.config.freespace_ignore_reserved = true;
724 break; 666 break;
725 case 'g': 667 case 'g':
726 if (path_selected) 668 if (path_selected) {
727 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); 669 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
670 _("Must set group value before selecting paths\n"));
671 }
728 group = optarg; 672 group = optarg;
729 break; 673 break;
730 case 'I': 674 case 'I':
731 cflags |= REG_ICASE; 675 cflags |= REG_ICASE;
732 // Intentional fallthrough 676 // Intentional fallthrough
733 case 'i': 677 case 'i': {
734 if (!path_selected) 678 if (!path_selected) {
735 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), 679 die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"),
736 _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); 680 _("Paths need to be selected before using -i/-I. Use -A to select all paths "
737 err = regcomp(&re, optarg, cflags); 681 "explicitly"));
682 }
683 regex_t regex;
684 int err = regcomp(&regex, optarg, cflags);
738 if (err != 0) { 685 if (err != 0) {
739 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 686 char errbuf[MAX_INPUT_BUFFER];
740 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 687 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
688 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
689 _("Could not compile regular expression"), errbuf);
741 } 690 }
742 691
743 temp_list = path_select_list; 692 for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) {
693 if (elem->best_match) {
694 if (np_regex_match_mount_entry(elem->best_match, &regex)) {
744 695
745 previous = NULL; 696 if (verbose >= 3) {
746 while (temp_list) { 697 printf("ignoring %s matching regex\n", elem->name);
747 if (temp_list->best_match) { 698 }
748 if (np_regex_match_mount_entry(temp_list->best_match, &re)) {
749 699
750 if (verbose >= 3) 700 elem = mp_int_fs_list_del(&result.config.path_select_list, elem);
751 printf("ignoring %s matching regex\n", temp_list->name); 701 continue;
752
753 temp_list = np_del_parameter(temp_list, previous);
754 /* pointer to first element needs to be updated if first item gets deleted */
755 if (previous == NULL)
756 path_select_list = temp_list;
757 } else {
758 previous = temp_list;
759 temp_list = temp_list->name_next;
760 } 702 }
761 } else {
762 previous = temp_list;
763 temp_list = temp_list->name_next;
764 } 703 }
704
705 elem = mp_int_fs_list_get_next(elem);
765 } 706 }
766 707
767 cflags = default_cflags; 708 cflags = default_cflags;
768 break; 709 } break;
769
770 case 'n': 710 case 'n':
771 ignore_missing = true; 711 result.config.ignore_missing = true;
772 break; 712 break;
773 case 'A': 713 case 'A':
774 optarg = strdup(".*"); 714 optarg = strdup(".*");
@@ -776,80 +716,96 @@ int process_arguments(int argc, char **argv) {
776 case 'R': 716 case 'R':
777 cflags |= REG_ICASE; 717 cflags |= REG_ICASE;
778 // Intentional fallthrough 718 // Intentional fallthrough
779 case 'r': 719 case 'r': {
780 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || 720 if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent ||
781 warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || 721 crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
782 warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) {
783 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), 722 die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"),
784 _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); 723 _("Must set a threshold value before using -r/-R/-A "
724 "(--ereg-path/--eregi-path/--all)\n"));
785 } 725 }
786 726
787 err = regcomp(&re, optarg, cflags); 727 regex_t regex;
728 int err = regcomp(&regex, optarg, cflags);
788 if (err != 0) { 729 if (err != 0) {
789 regerror(err, &re, errbuf, MAX_INPUT_BUFFER); 730 char errbuf[MAX_INPUT_BUFFER];
790 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); 731 regerror(err, &regex, errbuf, MAX_INPUT_BUFFER);
732 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
733 _("Could not compile regular expression"), errbuf);
791 } 734 }
792 735
793 for (me = mount_list; me; me = me->me_next) { 736 bool found = false;
794 if (np_regex_match_mount_entry(me, &re)) { 737 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
795 fnd = true; 738 if (np_regex_match_mount_entry(me, &regex)) {
796 if (verbose >= 3) 739 found = true;
797 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); 740 if (verbose >= 3) {
741 printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir,
742 optarg);
743 }
798 744
799 /* add parameter if not found. overwrite thresholds if path has already been added */ 745 /* add parameter if not found. overwrite thresholds if path has already been
800 if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { 746 * added */
801 se = np_add_parameter(&path_select_list, me->me_mountdir); 747 parameter_list_elem *se = NULL;
748 if (!(se = mp_int_fs_list_find(result.config.path_select_list,
749 me->me_mountdir))) {
750 se =
751 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
802 } 752 }
803 se->group = group; 753 se->group = group;
804 set_all_thresholds(se); 754 set_all_thresholds(se, warn_freespace_units, crit_freespace_units,
755 warn_freespace_percent, crit_freespace_percent,
756 warn_freeinodes_percent, crit_freeinodes_percent);
805 } 757 }
806 } 758 }
807 759
808 if (!fnd && ignore_missing == true) { 760 if (!found) {
809 path_ignored = true; 761 if (result.config.ignore_missing) {
810 path_selected = true; 762 result.config.path_ignored = true;
811 break; 763 path_selected = true;
764 break;
765 }
766
767 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"),
768 _("Regular expression did not match any path or disk"), optarg);
812 } 769 }
813 if (!fnd)
814 die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg);
815 770
816 fnd = false;
817 path_selected = true; 771 path_selected = true;
818 np_set_best_match(path_select_list, mount_list, exact_match); 772 mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list,
773 result.config.exact_match);
819 cflags = default_cflags; 774 cflags = default_cflags;
820 775
821 break; 776 } break;
822 case 'M': /* display mountpoint */ 777 case 'M': /* display mountpoint */
823 display_mntp = true; 778 result.config.display_mntp = true;
824 break; 779 break;
825 case 'C': 780 case 'C': {
826 /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ 781 /* add all mount entries to path_select list if no partitions have been explicitly
827 if (path_selected == false) { 782 * defined using -p */
828 struct parameter_list *path; 783 if (!path_selected) {
829 for (me = mount_list; me; me = me->me_next) { 784 parameter_list_elem *path;
830 if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) 785 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
831 path = np_add_parameter(&path_select_list, me->me_mountdir); 786 if (!(path = mp_int_fs_list_find(result.config.path_select_list,
787 me->me_mountdir))) {
788 path =
789 mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
790 }
832 path->best_match = me; 791 path->best_match = me;
833 path->group = group; 792 path->group = group;
834 set_all_thresholds(path); 793 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
794 warn_freespace_percent, crit_freespace_percent,
795 warn_freeinodes_percent, crit_freeinodes_percent);
835 } 796 }
836 } 797 }
798
837 warn_freespace_units = NULL; 799 warn_freespace_units = NULL;
838 crit_freespace_units = NULL; 800 crit_freespace_units = NULL;
839 warn_usedspace_units = NULL;
840 crit_usedspace_units = NULL;
841 warn_freespace_percent = NULL; 801 warn_freespace_percent = NULL;
842 crit_freespace_percent = NULL; 802 crit_freespace_percent = NULL;
843 warn_usedspace_percent = NULL;
844 crit_usedspace_percent = NULL;
845 warn_usedinodes_percent = NULL;
846 crit_usedinodes_percent = NULL;
847 warn_freeinodes_percent = NULL; 803 warn_freeinodes_percent = NULL;
848 crit_freeinodes_percent = NULL; 804 crit_freeinodes_percent = NULL;
849 805
850 path_selected = false; 806 path_selected = false;
851 group = NULL; 807 group = NULL;
852 break; 808 } break;
853 case 'V': /* version */ 809 case 'V': /* version */
854 print_revision(progname, NP_VERSION); 810 print_revision(progname, NP_VERSION);
855 exit(STATE_UNKNOWN); 811 exit(STATE_UNKNOWN);
@@ -858,50 +814,152 @@ int process_arguments(int argc, char **argv) {
858 exit(STATE_UNKNOWN); 814 exit(STATE_UNKNOWN);
859 case '?': /* help */ 815 case '?': /* help */
860 usage(_("Unknown argument")); 816 usage(_("Unknown argument"));
817 case output_format_index: {
818 parsed_output_format parser = mp_parse_output_format(optarg);
819 if (!parser.parsing_success) {
820 // TODO List all available formats here, maybe add anothoer usage function
821 printf("Invalid output format: %s\n", optarg);
822 exit(STATE_UNKNOWN);
823 }
824
825 result.config.output_format_is_set = true;
826 result.config.output_format = parser.output_format;
827 break;
828 }
861 } 829 }
862 } 830 }
863 831
864 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ 832 /* Support for "check_disk warn crit [fs]" with thresholds at used% level */
865 c = optind; 833 int index = optind;
866 if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 834
867 warn_usedspace_percent = argv[c++]; 835 if (argc > index && is_intnonneg(argv[index])) {
836 if (verbose > 0) {
837 printf("Got an positional warn threshold: %s\n", argv[index]);
838 }
839 char *range = argv[index++];
840 mp_range_parsed tmp = mp_parse_range_string(range);
841 if (tmp.error != MP_PARSING_SUCCES) {
842 die(STATE_UNKNOWN, "failed to parse warning threshold");
843 }
844
845 mp_range tmp_range = tmp.range;
846 // Invert range to use it for free instead of used
847 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
868 848
869 if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) 849 warn_freespace_percent = mp_range_to_string(tmp_range);
870 crit_usedspace_percent = argv[c++];
871 850
872 if (argc > c) { 851 if (verbose > 0) {
873 se = np_add_parameter(&path_select_list, strdup(argv[c++])); 852 printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent);
853 }
854 }
855
856 if (argc > index && is_intnonneg(argv[index])) {
857 if (verbose > 0) {
858 printf("Got an positional crit threshold: %s\n", argv[index]);
859 }
860 char *range = argv[index++];
861 mp_range_parsed tmp = mp_parse_range_string(range);
862 if (tmp.error != MP_PARSING_SUCCES) {
863 die(STATE_UNKNOWN, "failed to parse warning threshold");
864 }
865
866 mp_range tmp_range = tmp.range;
867 // Invert range to use it for free instead of used
868 // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range;
869
870 crit_freespace_percent = mp_range_to_string(tmp_range);
871
872 if (verbose > 0) {
873 printf("Positional critical threshold transformed to: %s\n", crit_freespace_percent);
874 }
875 }
876
877 if (argc > index) {
878 if (verbose > 0) {
879 printf("Got an positional filesystem: %s\n", argv[index]);
880 }
881 struct parameter_list *se =
882 mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++]));
874 path_selected = true; 883 path_selected = true;
875 set_all_thresholds(se); 884 set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent,
885 crit_freespace_percent, warn_freeinodes_percent,
886 crit_freeinodes_percent);
876 } 887 }
877 888
878 if (units == NULL) { 889 // If a list of paths has not been explicitly selected, find entire
879 units = strdup("MiB"); 890 // mount list and create list of paths
880 mult = (uintmax_t)1024 * 1024; 891 if (!path_selected && !result.config.path_ignored) {
892 for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) {
893 if (me->me_dummy != 0) {
894 // just do not add dummy filesystems
895 continue;
896 }
897
898 parameter_list_elem *path = NULL;
899 if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) {
900 path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir);
901 }
902 path->best_match = me;
903 path->group = group;
904 set_all_thresholds(path, warn_freespace_units, crit_freespace_units,
905 warn_freespace_percent, crit_freespace_percent,
906 warn_freeinodes_percent, crit_freeinodes_percent);
907 }
881 } 908 }
882 909
883 return true; 910 // Set thresholds to the appropriate unit
911 for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp;
912 tmp = mp_int_fs_list_get_next(tmp)) {
913
914 mp_perfdata_value factor = mp_create_pd_value(unit);
915
916 if (tmp->freespace_units.critical_is_set) {
917 tmp->freespace_units.critical =
918 mp_range_multiply(tmp->freespace_units.critical, factor);
919 }
920 if (tmp->freespace_units.warning_is_set) {
921 tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor);
922 }
923 }
924
925 return result;
884} 926}
885 927
886void set_all_thresholds(struct parameter_list *path) { 928void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units,
887 if (path->freespace_units != NULL) 929 char *crit_freespace_units, char *warn_freespace_percent,
888 free(path->freespace_units); 930 char *crit_freespace_percent, char *warn_freeinodes_percent,
889 set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); 931 char *crit_freeinodes_percent) {
890 if (path->freespace_percent != NULL) 932 mp_range_parsed tmp;
891 free(path->freespace_percent); 933
892 set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); 934 if (warn_freespace_units) {
893 if (path->usedspace_units != NULL) 935 tmp = mp_parse_range_string(warn_freespace_units);
894 free(path->usedspace_units); 936 path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range);
895 set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); 937 }
896 if (path->usedspace_percent != NULL) 938
897 free(path->usedspace_percent); 939 if (crit_freespace_units) {
898 set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); 940 tmp = mp_parse_range_string(crit_freespace_units);
899 if (path->usedinodes_percent != NULL) 941 path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range);
900 free(path->usedinodes_percent); 942 }
901 set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); 943
902 if (path->freeinodes_percent != NULL) 944 if (warn_freespace_percent) {
903 free(path->freeinodes_percent); 945 tmp = mp_parse_range_string(warn_freespace_percent);
904 set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); 946 path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range);
947 }
948
949 if (crit_freespace_percent) {
950 tmp = mp_parse_range_string(crit_freespace_percent);
951 path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range);
952 }
953
954 if (warn_freeinodes_percent) {
955 tmp = mp_parse_range_string(warn_freeinodes_percent);
956 path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range);
957 }
958
959 if (crit_freeinodes_percent) {
960 tmp = mp_parse_range_string(crit_freeinodes_percent);
961 path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range);
962 }
905} 963}
906 964
907void print_help(void) { 965void print_help(void) {
@@ -911,7 +969,8 @@ void print_help(void) {
911 printf(COPYRIGHT, copyright, email); 969 printf(COPYRIGHT, copyright, email);
912 970
913 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system")); 971 printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system"));
914 printf("%s\n", _("and generates an alert if free space is less than one of the threshold values")); 972 printf("%s\n",
973 _("and generates an alert if free space is less than one of the threshold values"));
915 974
916 printf("\n\n"); 975 printf("\n\n");
917 976
@@ -933,7 +992,8 @@ void print_help(void) {
933 printf(" %s\n", "-K, --icritical=PERCENT%"); 992 printf(" %s\n", "-K, --icritical=PERCENT%");
934 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); 993 printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free"));
935 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION"); 994 printf(" %s\n", "-p, --path=PATH, --partition=PARTITION");
936 printf(" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)")); 995 printf(" %s\n",
996 _("Mount point or block device as emitted by the mount(8) command (may be repeated)"));
937 printf(" %s\n", "-x, --exclude_device=PATH <STRING>"); 997 printf(" %s\n", "-x, --exclude_device=PATH <STRING>");
938 printf(" %s\n", _("Ignore device (only works if -p unspecified)")); 998 printf(" %s\n", _("Ignore device (only works if -p unspecified)"));
939 printf(" %s\n", "-C, --clear"); 999 printf(" %s\n", "-C, --clear");
@@ -947,167 +1007,298 @@ void print_help(void) {
947 printf(" %s\n", "-P, --iperfdata"); 1007 printf(" %s\n", "-P, --iperfdata");
948 printf(" %s\n", _("Display inode usage in perfdata")); 1008 printf(" %s\n", _("Display inode usage in perfdata"));
949 printf(" %s\n", "-g, --group=NAME"); 1009 printf(" %s\n", "-g, --group=NAME");
950 printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); 1010 printf(" %s\n",
951 printf(" %s\n", "-k, --kilobytes"); 1011 _("Group paths. Thresholds apply to (free-)space of all partitions together"));
952 printf(" %s\n", _("Same as '--units kB'"));
953 printf(" %s\n", "-l, --local"); 1012 printf(" %s\n", "-l, --local");
954 printf(" %s\n", _("Only check local filesystems")); 1013 printf(" %s\n", _("Only check local filesystems"));
955 printf(" %s\n", "-L, --stat-remote-fs"); 1014 printf(" %s\n", "-L, --stat-remote-fs");
956 printf(" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); 1015 printf(
1016 " %s\n",
1017 _("Only check local filesystems against thresholds. Yet call stat on remote filesystems"));
957 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); 1018 printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)"));
958 printf(" %s\n", "-M, --mountpoint"); 1019 printf(" %s\n", "-M, --mountpoint");
959 printf(" %s\n", _("Display the (block) device instead of the mount point")); 1020 printf(" %s\n", _("Display the (block) device instead of the mount point"));
960 printf(" %s\n", "-m, --megabytes");
961 printf(" %s\n", _("Same as '--units MB'"));
962 printf(" %s\n", "-A, --all"); 1021 printf(" %s\n", "-A, --all");
963 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); 1022 printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'"));
964 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); 1023 printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION");
965 printf(" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)")); 1024 printf(" %s\n",
1025 _("Case insensitive regular expression for path/partition (may be repeated)"));
966 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION"); 1026 printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION");
967 printf(" %s\n", _("Regular expression for path or partition (may be repeated)")); 1027 printf(" %s\n", _("Regular expression for path or partition (may be repeated)"));
968 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION"); 1028 printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION");
969 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)")); 1029 printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) "
1030 "(may be repeated)"));
970 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION"); 1031 printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION");
971 printf(" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)")); 1032 printf(" %s\n",
1033 _("Regular expression to ignore selected path or partition (may be repeated)"));
972 printf(" %s\n", "-n, --ignore-missing"); 1034 printf(" %s\n", "-n, --ignore-missing");
973 printf(" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible.")); 1035 printf(" %s\n",
1036 _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible."));
974 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); 1037 printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)"));
975 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); 1038 printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
976 printf(" %s\n", "-u, --units=STRING"); 1039 printf(" %s\n", "-u, --units=STRING");
977 printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); 1040 printf(" %s\n", _("Select the unit used for the absolute value thresholds"));
1041 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1042 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1043 printf(" %s\n", "-k, --kilobytes");
1044 printf(" %s\n", _("Same as '--units kB'"));
1045 printf(" %s\n", "--display-unit");
1046 printf(" %s\n", _("Select the unit used for in the output"));
1047 printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", "
1048 "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)"));
1049 printf(" %s\n", "-m, --megabytes");
1050 printf(" %s\n", _("Same as '--units MB'"));
978 printf(UT_VERBOSE); 1051 printf(UT_VERBOSE);
979 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); 1052 printf(" %s\n", "-X, --exclude-type=TYPE_REGEX");
980 printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); 1053 printf(" %s\n",
1054 _("Ignore all filesystems of types matching given regex(7) (may be repeated)"));
981 printf(" %s\n", "-N, --include-type=TYPE_REGEX"); 1055 printf(" %s\n", "-N, --include-type=TYPE_REGEX");
982 printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); 1056 printf(
1057 " %s\n",
1058 _("Check only filesystems where the type matches this given regex(7) (may be repeated)"));
1059 printf(UT_OUTPUT_FORMAT);
983 1060
984 printf("\n"); 1061 printf("\n");
985 printf("%s\n", _("General usage hints:")); 1062 printf("%s\n", _("General usage hints:"));
986 printf(" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as")); 1063 printf(
1064 " %s\n",
1065 _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as"));
987 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\".")); 1066 printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\"."));
988 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\"")); 1067 printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} "
1068 "{thresholds b} ...\""));
989 1069
990 printf("\n"); 1070 printf("\n");
991 printf("%s\n", _("Examples:")); 1071 printf("%s\n", _("Examples:"));
992 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /"); 1072 printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /");
993 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB")); 1073 printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB"));
994 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'"); 1074 printf(" %s\n",
995 printf(" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); 1075 "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'");
996 printf(" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together")); 1076 printf(
1077 " %s\n",
1078 _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex"));
1079 printf(" %s\n\n",
1080 _("are grouped which means the freespace thresholds are applied to all disks together"));
997 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar"); 1081 printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar");
998 printf(" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); 1082 printf(" %s\n",
1083 _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M"));
999 1084
1000 printf(UT_SUPPORT); 1085 printf(UT_SUPPORT);
1001} 1086}
1002 1087
1003void print_usage(void) { 1088void print_usage(void) {
1004 printf("%s\n", _("Usage:")); 1089 printf("%s\n", _("Usage:"));
1005 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K " 1090 printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c "
1091 "absolute_limit|-c percentage_limit%% | -K "
1006 "inode_percentage_limit } {-p path | -x device}\n", 1092 "inode_percentage_limit } {-p path | -x device}\n",
1007 progname); 1093 progname);
1008 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); 1094 printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n");
1009 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); 1095 printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n");
1010} 1096}
1011 1097
1012bool stat_path(struct parameter_list *p) { 1098bool stat_path(parameter_list_elem *parameters, bool ignore_missing) {
1013 /* Stat entry to check that dir exists and is accessible */ 1099 /* Stat entry to check that dir exists and is accessible */
1014 if (verbose >= 3) 1100 if (verbose >= 3) {
1015 printf("calling stat on %s\n", p->name); 1101 printf("calling stat on %s\n", parameters->name);
1016 if (stat(p->name, &stat_buf[0])) { 1102 }
1017 if (verbose >= 3) 1103
1018 printf("stat failed on %s\n", p->name); 1104 struct stat stat_buf = {0};
1019 if (ignore_missing == true) { 1105 if (stat(parameters->name, &stat_buf)) {
1106 if (verbose >= 3) {
1107 printf("stat failed on %s\n", parameters->name);
1108 }
1109 if (ignore_missing) {
1020 return false; 1110 return false;
1021 } 1111 }
1022 printf("DISK %s - ", _("CRITICAL")); 1112 printf("DISK %s - ", _("CRITICAL"));
1023 die(STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); 1113 die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"),
1114 strerror(errno));
1024 } 1115 }
1116
1025 return true; 1117 return true;
1026} 1118}
1027 1119
1028void get_stats(struct parameter_list *p, struct fs_usage *fsp) { 1120static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp,
1029 struct parameter_list *p_list; 1121 bool freespace_ignore_reserved) {
1030 struct fs_usage tmpfsp; 1122 uintmax_t available = fsp.fsu_bavail;
1031 int first = 1; 1123 uintmax_t available_to_root = fsp.fsu_bfree;
1124 uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree;
1125 uintmax_t total;
1032 1126
1033 if (p->group == NULL) {
1034 get_path_stats(p, fsp);
1035 } else {
1036 /* find all group members */
1037 for (p_list = path_select_list; p_list; p_list = p_list->name_next) {
1038#ifdef __CYGWIN__
1039 if (strncmp(p_list->name, "/cygdrive/", 10) != 0)
1040 continue;
1041#endif
1042 if (p_list->group && !(strcmp(p_list->group, p->group))) {
1043 if (!stat_path(p_list))
1044 continue;
1045 get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp);
1046 get_path_stats(p_list, &tmpfsp);
1047 if (verbose >= 3)
1048 printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n",
1049 p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units,
1050 p_list->dfree_units, p_list->dtotal_units, mult);
1051
1052 /* prevent counting the first FS of a group twice since its parameter_list entry
1053 * is used to carry the information of all file systems of the entire group */
1054 if (!first) {
1055 p->total += p_list->total;
1056 p->available += p_list->available;
1057 p->available_to_root += p_list->available_to_root;
1058 p->used += p_list->used;
1059
1060 p->dused_units += p_list->dused_units;
1061 p->dfree_units += p_list->dfree_units;
1062 p->dtotal_units += p_list->dtotal_units;
1063 p->inodes_total += p_list->inodes_total;
1064 p->inodes_free += p_list->inodes_free;
1065 p->inodes_free_to_root += p_list->inodes_free_to_root;
1066 p->inodes_used += p_list->inodes_used;
1067 }
1068 first = 0;
1069 }
1070 if (verbose >= 3)
1071 printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p->group,
1072 p->dused_units, p->dfree_units, p->dtotal_units, tmpfsp.fsu_blocksize, mult);
1073 }
1074 /* modify devname and mountdir for output */
1075 p->best_match->me_mountdir = p->best_match->me_devname = p->group;
1076 }
1077 /* finally calculate percentages for either plain FS or summed up group */
1078 p->dused_pct = calculate_percent(p->used, p->used + p->available); /* used + available can never be > uintmax */
1079 p->dfree_pct = 100.0 - p->dused_pct;
1080 p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total);
1081 p->dfree_inodes_percent = 100 - p->dused_inodes_percent;
1082}
1083
1084void get_path_stats(struct parameter_list *p, struct fs_usage *fsp) {
1085 p->available = fsp->fsu_bavail;
1086 p->available_to_root = fsp->fsu_bfree;
1087 p->used = fsp->fsu_blocks - fsp->fsu_bfree;
1088 if (freespace_ignore_reserved) { 1127 if (freespace_ignore_reserved) {
1089 /* option activated : we subtract the root-reserved space from the total */ 1128 /* option activated : we subtract the root-reserved space from the total */
1090 p->total = fsp->fsu_blocks - p->available_to_root + p->available; 1129 total = fsp.fsu_blocks - available_to_root + available;
1091 } else { 1130 } else {
1092 /* default behaviour : take all the blocks into account */ 1131 /* default behaviour : take all the blocks into account */
1093 p->total = fsp->fsu_blocks; 1132 total = fsp.fsu_blocks;
1094 } 1133 }
1095 1134
1096 p->dused_units = p->used * fsp->fsu_blocksize / mult; 1135 parameters.used_bytes = used * fsp.fsu_blocksize;
1097 p->dfree_units = p->available * fsp->fsu_blocksize / mult; 1136 parameters.free_bytes = available * fsp.fsu_blocksize;
1098 p->dtotal_units = p->total * fsp->fsu_blocksize / mult; 1137 parameters.total_bytes = total * fsp.fsu_blocksize;
1138
1099 /* Free file nodes. Not sure the workaround is required, but in case...*/ 1139 /* Free file nodes. Not sure the workaround is required, but in case...*/
1100 p->inodes_free = fsp->fsu_ffree; 1140 parameters.inodes_free = fsp.fsu_ffree;
1101 p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ 1141 parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */
1102 p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; 1142 parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree;
1143
1103 if (freespace_ignore_reserved) { 1144 if (freespace_ignore_reserved) {
1104 /* option activated : we subtract the root-reserved inodes from the total */ 1145 /* option activated : we subtract the root-reserved inodes from the total */
1105 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ 1146 /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */
1106 /* for others, fsp->fsu_ffree == fsp->fsu_favail */ 1147 /* for others, fsp->fsu_ffree == fsp->fsu_favail */
1107 p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; 1148 parameters.inodes_total =
1149 fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free;
1108 } else { 1150 } else {
1109 /* default behaviour : take all the inodes into account */ 1151 /* default behaviour : take all the inodes into account */
1110 p->inodes_total = fsp->fsu_files; 1152 parameters.inodes_total = fsp.fsu_files;
1111 } 1153 }
1112 np_add_name(&seen, p->best_match->me_mountdir); 1154
1155 return parameters;
1156}
1157
1158mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata,
1159 byte_unit unit) {
1160 mp_subcheck result = mp_subcheck_init();
1161 result = mp_set_subcheck_default_state(result, STATE_UNKNOWN);
1162 xasprintf(&result.output, "%s", measurement_unit.name);
1163
1164 if (!measurement_unit.is_group && measurement_unit.filesystem_type) {
1165 xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type);
1166 }
1167
1168 /* Threshold comparisons */
1169
1170 // ===============================
1171 // Free space absolute values test
1172 mp_subcheck freespace_bytes_sc = mp_subcheck_init();
1173 freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK);
1174
1175 if (unit != Humanized) {
1176 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)",
1177 (uintmax_t)(measurement_unit.free_bytes / unit), get_unit_string(unit),
1178 (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit));
1179 } else {
1180 xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)",
1181 humanize_byte_value(measurement_unit.free_bytes, false),
1182 humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false));
1183 }
1184
1185 mp_perfdata used_space = perfdata_init();
1186 used_space.label = measurement_unit.name;
1187 used_space.value = mp_create_pd_value(measurement_unit.free_bytes);
1188 used_space = mp_set_pd_max_value(used_space, mp_create_pd_value(measurement_unit.total_bytes));
1189 used_space = mp_set_pd_min_value(used_space, mp_create_pd_value(0));
1190 used_space.uom = "B";
1191 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1192 freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(used_space));
1193
1194 // special case for absolute space thresholds here:
1195 // if absolute values are not set, compute the thresholds from percentage thresholds
1196 mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds;
1197 if (!temp_thlds.critical_is_set &&
1198 measurement_unit.freespace_percent_thresholds.critical_is_set) {
1199 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical;
1200
1201 if (!tmp_range.end_infinity) {
1202 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1203 measurement_unit.total_bytes);
1204 }
1205
1206 if (!tmp_range.start_infinity) {
1207 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1208 measurement_unit.total_bytes);
1209 }
1210 measurement_unit.freespace_bytes_thresholds =
1211 mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range);
1212 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1213 }
1214
1215 if (!temp_thlds.warning_is_set &&
1216 measurement_unit.freespace_percent_thresholds.warning_is_set) {
1217 mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning;
1218 if (!tmp_range.end_infinity) {
1219 tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 *
1220 measurement_unit.total_bytes);
1221 }
1222 if (!tmp_range.start_infinity) {
1223 tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 *
1224 measurement_unit.total_bytes);
1225 }
1226 measurement_unit.freespace_bytes_thresholds =
1227 mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range);
1228 used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds);
1229 }
1230
1231 mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space);
1232 mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc);
1233
1234 // ==========================
1235 // Free space percentage test
1236 mp_subcheck freespace_percent_sc = mp_subcheck_init();
1237 freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK);
1238
1239 double free_percentage =
1240 calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes);
1241 xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage);
1242
1243 // Using perfdata here just to get to the test result
1244 mp_perfdata free_space_percent_pd = perfdata_init();
1245 free_space_percent_pd.value = mp_create_pd_value(free_percentage);
1246 free_space_percent_pd =
1247 mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds);
1248
1249 freespace_percent_sc =
1250 mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd));
1251 mp_add_subcheck_to_subcheck(&result, freespace_percent_sc);
1252
1253 // ================
1254 // Free inodes test
1255 // Only ever useful if the number of inodes is static (e.g. ext4),
1256 // not when it is dynamic (e.g btrfs)
1257 // Assumption: if the total number of inodes == 0, we have such a case and just skip the test
1258 if (measurement_unit.inodes_total > 0) {
1259 mp_subcheck freeindodes_percent_sc = mp_subcheck_init();
1260 freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK);
1261
1262 double free_inode_percentage =
1263 calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total);
1264
1265 if (verbose > 0) {
1266 printf("free inode percentage computed: %g\n", free_inode_percentage);
1267 }
1268
1269 xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)",
1270 free_inode_percentage, measurement_unit.inodes_free,
1271 measurement_unit.inodes_total);
1272
1273 mp_perfdata inodes_pd = perfdata_init();
1274 xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name);
1275 inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used);
1276 inodes_pd =
1277 mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total));
1278 inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0));
1279
1280 mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds;
1281
1282 if (absolut_inode_thresholds.critical_is_set) {
1283 absolut_inode_thresholds.critical =
1284 mp_range_multiply(absolut_inode_thresholds.critical,
1285 mp_create_pd_value(measurement_unit.inodes_total / 100));
1286 }
1287 if (absolut_inode_thresholds.warning_is_set) {
1288 absolut_inode_thresholds.warning =
1289 mp_range_multiply(absolut_inode_thresholds.warning,
1290 mp_create_pd_value(measurement_unit.inodes_total / 100));
1291 }
1292
1293 inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds);
1294
1295 freeindodes_percent_sc =
1296 mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd));
1297 if (display_inodes_perfdata) {
1298 mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd);
1299 }
1300 mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc);
1301 }
1302
1303 return result;
1113} 1304}