diff options
Diffstat (limited to 'plugins/check_disk.d/utils_disk.c')
-rw-r--r-- | plugins/check_disk.d/utils_disk.c | 517 |
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 | |||
39 | void 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 | */ | ||
56 | int 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 | |||
78 | parameter_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 */ | ||
105 | bool 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 */ | ||
118 | bool 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 | |||
137 | bool 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 | |||
146 | bool 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 | |||
150 | check_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 | |||
183 | char *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 | |||
216 | measurement_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 | ||
240 | measurement_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 | |||
267 | measurement_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 | |||
280 | measurement_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 | |||
312 | char *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 | |||
372 | filesystem_list filesystem_list_init() { | ||
373 | filesystem_list tmp = { | ||
374 | .length = 0, | ||
375 | .first = NULL, | ||
376 | }; | ||
377 | return tmp; | ||
378 | } | ||
379 | |||
380 | parameter_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 | |||
400 | parameter_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 | |||
414 | parameter_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 | |||
465 | parameter_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 | |||
472 | void 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 | } | ||