summaryrefslogtreecommitdiffstats
path: root/plugins/check_disk.d/utils_disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_disk.d/utils_disk.c')
-rw-r--r--plugins/check_disk.d/utils_disk.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c
new file mode 100644
index 00000000..eec1282b
--- /dev/null
+++ b/plugins/check_disk.d/utils_disk.c
@@ -0,0 +1,517 @@
1/*****************************************************************************
2 *
3 * Library for check_disk
4 *
5 * License: GPL
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains utilities for check_disk. These are tested by libtap
11 *
12 *
13 * This program is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 *
27 *****************************************************************************/
28
29#include "../common.h"
30#include "utils_disk.h"
31#include "../../gl/fsusage.h"
32#include "../../lib/thresholds.h"
33#include "../../lib/states.h"
34#include <stdint.h>
35#include <stdio.h>
36#include <string.h>
37#include <assert.h>
38
39void np_add_name(struct name_list **list, const char *name) {
40 struct name_list *new_entry;
41 new_entry = (struct name_list *)malloc(sizeof *new_entry);
42 new_entry->name = (char *)name;
43 new_entry->next = *list;
44 *list = new_entry;
45}
46
47/* @brief Initialises a new regex at the begin of list via regcomp(3)
48 *
49 * @details if the regex fails to compile the error code of regcomp(3) is returned
50 * and list is not modified, otherwise list is modified to point to the new
51 * element
52 * @param list Pointer to a linked list of regex_list elements
53 * @param regex the string containing the regex which should be inserted into the list
54 * @param clags the cflags parameter for regcomp(3)
55 */
56int np_add_regex(struct regex_list **list, const char *regex, int cflags) {
57 struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry);
58
59 if (new_entry == NULL) {
60 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
61 }
62
63 int regcomp_result = regcomp(&new_entry->regex, regex, cflags);
64
65 if (!regcomp_result) {
66 // regcomp succeeded
67 new_entry->next = *list;
68 *list = new_entry;
69
70 return 0;
71 }
72 // regcomp failed
73 free(new_entry);
74
75 return regcomp_result;
76}
77
78parameter_list_elem parameter_list_init(const char *name) {
79 parameter_list_elem result = {
80 .name = strdup(name),
81 .best_match = NULL,
82
83 .freespace_units = mp_thresholds_init(),
84 .freespace_percent = mp_thresholds_init(),
85 .freeinodes_percent = mp_thresholds_init(),
86
87 .group = NULL,
88
89 .inodes_total = 0,
90 .inodes_free = 0,
91 .inodes_free_to_root = 0,
92 .inodes_used = 0,
93
94 .used_bytes = 0,
95 .free_bytes = 0,
96 .total_bytes = 0,
97
98 .next = NULL,
99 .prev = NULL,
100 };
101 return result;
102}
103
104/* Returns true if name is in list */
105bool np_find_name(struct name_list *list, const char *name) {
106 if (list == NULL || name == NULL) {
107 return false;
108 }
109 for (struct name_list *iterator = list; iterator; iterator = iterator->next) {
110 if (!strcmp(name, iterator->name)) {
111 return true;
112 }
113 }
114 return false;
115}
116
117/* Returns true if name is in list */
118bool np_find_regmatch(struct regex_list *list, const char *name) {
119 if (name == NULL) {
120 return false;
121 }
122
123 size_t len = strlen(name);
124
125 for (; list; list = list->next) {
126 /* Emulate a full match as if surrounded with ^( )$
127 by checking whether the match spans the whole name */
128 regmatch_t dummy_match;
129 if (!regexec(&list->regex, name, 1, &dummy_match, 0) && dummy_match.rm_so == 0 && dummy_match.rm_eo == len) {
130 return true;
131 }
132 }
133
134 return false;
135}
136
137bool np_seen_name(struct name_list *list, const char *name) {
138 for (struct name_list *iterator = list; iterator; iterator = iterator->next) {
139 if (!strcmp(iterator->name, name)) {
140 return true;
141 }
142 }
143 return false;
144}
145
146bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) {
147 return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0));
148}
149
150check_disk_config check_disk_config_init() {
151 check_disk_config tmp = {
152 .erronly = false,
153 .display_mntp = false,
154 .show_local_fs = false,
155 .stat_remote_fs = false,
156 .display_inodes_perfdata = false,
157
158 .exact_match = false,
159 .freespace_ignore_reserved = false,
160
161 .ignore_missing = false,
162 .path_ignored = false,
163
164 // FS Filters
165 .fs_exclude_list = NULL,
166 .fs_include_list = NULL,
167 .device_path_exclude_list = NULL,
168
169 // Actual filesystems paths to investigate
170 .path_select_list = filesystem_list_init(),
171
172 .mount_list = NULL,
173 .seen = NULL,
174
175 .display_unit = Humanized,
176 // .unit = MebiBytes,
177
178 .output_format_is_set = false,
179 };
180 return tmp;
181}
182
183char *get_unit_string(byte_unit_enum unit) {
184 switch (unit) {
185 case Bytes:
186 return "Bytes";
187 case KibiBytes:
188 return "KiB";
189 case MebiBytes:
190 return "MiB";
191 case GibiBytes:
192 return "GiB";
193 case TebiBytes:
194 return "TiB";
195 case PebiBytes:
196 return "PiB";
197 case ExbiBytes:
198 return "EiB";
199 case KiloBytes:
200 return "KB";
201 case MegaBytes:
202 return "MB";
203 case GigaBytes:
204 return "GB";
205 case TeraBytes:
206 return "TB";
207 case PetaBytes:
208 return "PB";
209 case ExaBytes:
210 return "EB";
211 default:
212 assert(false);
213 }
214}
215
216measurement_unit measurement_unit_init() {
217 measurement_unit tmp = {
218 .name = NULL,
219 .filesystem_type = NULL,
220 .is_group = false,
221
222 .freeinodes_percent_thresholds = mp_thresholds_init(),
223 .freespace_percent_thresholds = mp_thresholds_init(),
224 .freespace_bytes_thresholds = mp_thresholds_init(),
225
226 .free_bytes = 0,
227 .used_bytes = 0,
228 .total_bytes = 0,
229
230 .inodes_total = 0,
231 .inodes_free = 0,
232 .inodes_free_to_root = 0,
233 .inodes_used = 0,
234 };
235 return tmp;
236}
237
238// Add a given element to the list, memory for the new element is freshly allocated
239// Returns a pointer to new element
240measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem) {
241 // find last element
242 measurement_unit_list *new = NULL;
243 if (list == NULL) {
244 new = calloc(1, sizeof(measurement_unit_list));
245 if (new == NULL) {
246 die(STATE_UNKNOWN, _("allocation failed"));
247 }
248 } else {
249 measurement_unit_list *list_elem = list;
250 while (list_elem->next != NULL) {
251 list_elem = list_elem->next;
252 }
253
254 new = calloc(1, sizeof(measurement_unit_list));
255 if (new == NULL) {
256 die(STATE_UNKNOWN, _("allocation failed"));
257 }
258
259 list_elem->next = new;
260 }
261
262 new->unit = elem;
263 new->next = NULL;
264 return new;
265}
266
267measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, parameter_list_elem filesystem) {
268
269 unit.free_bytes += filesystem.free_bytes;
270 unit.used_bytes += filesystem.used_bytes;
271 unit.total_bytes += filesystem.total_bytes;
272
273 unit.inodes_total += filesystem.inodes_total;
274 unit.inodes_free += filesystem.inodes_free;
275 unit.inodes_free_to_root += filesystem.inodes_free_to_root;
276 unit.inodes_used += filesystem.inodes_used;
277 return unit;
278}
279
280measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, bool display_mntp) {
281 measurement_unit result = measurement_unit_init();
282 if (!display_mntp) {
283 result.name = strdup(filesystem.best_match->me_mountdir);
284 } else {
285 result.name = strdup(filesystem.best_match->me_devname);
286 }
287
288 if (filesystem.group) {
289 result.is_group = true;
290 } else {
291 result.is_group = false;
292 if (filesystem.best_match) {
293 result.filesystem_type = filesystem.best_match->me_type;
294 }
295 }
296
297 result.freeinodes_percent_thresholds = filesystem.freeinodes_percent;
298 result.freespace_percent_thresholds = filesystem.freespace_percent;
299 result.freespace_bytes_thresholds = filesystem.freespace_units;
300 result.free_bytes = filesystem.free_bytes;
301 result.total_bytes = filesystem.total_bytes;
302 result.used_bytes = filesystem.used_bytes;
303 result.inodes_total = filesystem.inodes_total;
304 result.inodes_used = filesystem.inodes_used;
305 result.inodes_free = filesystem.inodes_free;
306 result.inodes_free_to_root = filesystem.inodes_free_to_root;
307 return result;
308}
309
310#define RANDOM_STRING_LENGTH 64
311
312char *humanize_byte_value(unsigned long long value, bool use_si_units) {
313 // Idea: A reasonable output should have at most 3 orders of magnitude
314 // before the decimal separator
315 // 353GiB is ok, 2444 GiB should be 2.386 TiB
316 char *result = calloc(RANDOM_STRING_LENGTH, sizeof(char));
317 if (result == NULL) {
318 die(STATE_UNKNOWN, _("allocation failed"));
319 }
320 const byte_unit KibiBytes_factor = 1024;
321 const byte_unit MebiBytes_factor = 1048576;
322 const byte_unit GibiBytes_factor = 1073741824;
323 const byte_unit TebiBytes_factor = 1099511627776;
324 const byte_unit PebiBytes_factor = 1125899906842624;
325 const byte_unit ExbiBytes_factor = 1152921504606846976;
326 const byte_unit KiloBytes_factor = 1000;
327 const byte_unit MegaBytes_factor = 1000000;
328 const byte_unit GigaBytes_factor = 1000000000;
329 const byte_unit TeraBytes_factor = 1000000000000;
330 const byte_unit PetaBytes_factor = 1000000000000000;
331 const byte_unit ExaBytes_factor = 1000000000000000000;
332
333 if (use_si_units) {
334 // SI units, powers of 10
335 if (value < KiloBytes_factor) {
336 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
337 } else if (value < MegaBytes_factor) {
338 snprintf(result, RANDOM_STRING_LENGTH, "%llu KB", value / KiloBytes_factor);
339 } else if (value < GigaBytes_factor) {
340 snprintf(result, RANDOM_STRING_LENGTH, "%llu MB", value / MegaBytes_factor);
341 } else if (value < TeraBytes_factor) {
342 snprintf(result, RANDOM_STRING_LENGTH, "%llu GB", value / GigaBytes_factor);
343 } else if (value < PetaBytes_factor) {
344 snprintf(result, RANDOM_STRING_LENGTH, "%llu TB", value / TeraBytes_factor);
345 } else if (value < ExaBytes_factor) {
346 snprintf(result, RANDOM_STRING_LENGTH, "%llu PB", value / PetaBytes_factor);
347 } else {
348 snprintf(result, RANDOM_STRING_LENGTH, "%llu EB", value / ExaBytes_factor);
349 }
350 } else {
351 // IEC units, powers of 2 ^ 10
352 if (value < KibiBytes_factor) {
353 snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value);
354 } else if (value < MebiBytes_factor) {
355 snprintf(result, RANDOM_STRING_LENGTH, "%llu KiB", value / KibiBytes_factor);
356 } else if (value < GibiBytes_factor) {
357 snprintf(result, RANDOM_STRING_LENGTH, "%llu MiB", value / MebiBytes_factor);
358 } else if (value < TebiBytes_factor) {
359 snprintf(result, RANDOM_STRING_LENGTH, "%llu GiB", value / GibiBytes_factor);
360 } else if (value < PebiBytes_factor) {
361 snprintf(result, RANDOM_STRING_LENGTH, "%llu TiB", value / TebiBytes_factor);
362 } else if (value < ExbiBytes_factor) {
363 snprintf(result, RANDOM_STRING_LENGTH, "%llu PiB", value / PebiBytes_factor);
364 } else {
365 snprintf(result, RANDOM_STRING_LENGTH, "%llu EiB", value / ExbiBytes_factor);
366 }
367 }
368
369 return result;
370}
371
372filesystem_list filesystem_list_init() {
373 filesystem_list tmp = {
374 .length = 0,
375 .first = NULL,
376 };
377 return tmp;
378}
379
380parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name) {
381 parameter_list_elem *current = list->first;
382 parameter_list_elem *new_path = (struct parameter_list *)malloc(sizeof *new_path);
383 *new_path = parameter_list_init(name);
384
385 if (current == NULL) {
386 list->first = new_path;
387 new_path->prev = NULL;
388 list->length = 1;
389 } else {
390 while (current->next) {
391 current = current->next;
392 }
393 current->next = new_path;
394 new_path->prev = current;
395 list->length++;
396 }
397 return new_path;
398}
399
400parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name) {
401 if (list.length == 0) {
402 return NULL;
403 }
404
405 for (parameter_list_elem *temp_list = list.first; temp_list; temp_list = temp_list->next) {
406 if (!strcmp(temp_list->name, name)) {
407 return temp_list;
408 }
409 }
410
411 return NULL;
412}
413
414parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item) {
415 if (list->length == 0) {
416 return NULL;
417 }
418
419 if (item == NULL) {
420 // Got NULL for item, interpret this as "delete first element"
421 // as a kind of compatibility to the old function
422 item = list->first;
423 }
424
425 if (list->first == item) {
426 list->length--;
427
428 list->first = item->next;
429 if (list->first) {
430 list->first->prev = NULL;
431 }
432 return list->first;
433 }
434
435 // Was not the first element, continue
436 parameter_list_elem *prev = list->first;
437 parameter_list_elem *current = list->first->next;
438
439 while (current != item && current != NULL) {
440 prev = current;
441 current = current->next;
442 }
443
444 if (current == NULL) {
445 // didn't find that element ....
446 return NULL;
447 }
448
449 // remove the element
450 parameter_list_elem *next = current->next;
451 prev->next = next;
452 list->length--;
453 if (next) {
454 next->prev = prev;
455 }
456
457 if (item->name) {
458 free(item->name);
459 }
460 free(item);
461
462 return next;
463}
464
465parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current) {
466 if (!current) {
467 return NULL;
468 }
469 return current->next;
470}
471
472void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, bool exact) {
473 for (parameter_list_elem *elem = list.first; elem; elem = mp_int_fs_list_get_next(elem)) {
474 if (!elem->best_match) {
475 size_t name_len = strlen(elem->name);
476 struct mount_entry *best_match = NULL;
477
478 /* set best match if path name exactly matches a mounted device name */
479 for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) {
480 if (strcmp(mount_entry->me_devname, elem->name) == 0) {
481 struct fs_usage fsp;
482 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) {
483 best_match = mount_entry;
484 }
485 }
486 }
487
488 /* set best match by directory name if no match was found by devname */
489 if (!best_match) {
490 size_t best_match_len = 0;
491 for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) {
492 size_t len = strlen(mount_entry->me_mountdir);
493
494 if ((!exact && (best_match_len <= len && len <= name_len &&
495 (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) ||
496 (exact && strcmp(mount_entry->me_mountdir, elem->name) == 0)) {
497 struct fs_usage fsp;
498
499 if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) {
500 best_match = mount_entry;
501 best_match_len = len;
502 }
503 }
504 }
505 }
506
507 if (best_match) {
508 elem->best_match = best_match;
509 } else {
510 elem->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */
511 }
512
513 // No filesystem without a mount_entry!
514 // assert(elem->best_match != NULL);
515 }
516 }
517}