summaryrefslogtreecommitdiffstats
path: root/lib/utils_base.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utils_base.c')
-rw-r--r--lib/utils_base.c438
1 files changed, 45 insertions, 393 deletions
diff --git a/lib/utils_base.c b/lib/utils_base.c
index c49a473f..29b393d0 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -33,12 +33,12 @@
33#include <unistd.h> 33#include <unistd.h>
34#include <sys/types.h> 34#include <sys/types.h>
35 35
36#define np_free(ptr) \ 36#define np_free(ptr) \
37 { \ 37 { \
38 if (ptr) { \ 38 if (ptr) { \
39 free(ptr); \ 39 free(ptr); \
40 ptr = NULL; \ 40 ptr = NULL; \
41 } \ 41 } \
42 } 42 }
43 43
44monitoring_plugin *this_monitoring_plugin = NULL; 44monitoring_plugin *this_monitoring_plugin = NULL;
@@ -46,7 +46,7 @@ monitoring_plugin *this_monitoring_plugin = NULL;
46int timeout_state = STATE_CRITICAL; 46int timeout_state = STATE_CRITICAL;
47unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; 47unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
48 48
49bool _np_state_read_file(FILE *); 49bool _np_state_read_file(FILE *state_file);
50 50
51void np_init(char *plugin_name, int argc, char **argv) { 51void np_init(char *plugin_name, int argc, char **argv) {
52 if (this_monitoring_plugin == NULL) { 52 if (this_monitoring_plugin == NULL) {
@@ -74,14 +74,6 @@ void np_set_args(int argc, char **argv) {
74 74
75void np_cleanup(void) { 75void np_cleanup(void) {
76 if (this_monitoring_plugin != NULL) { 76 if (this_monitoring_plugin != NULL) {
77 if (this_monitoring_plugin->state != NULL) {
78 if (this_monitoring_plugin->state->state_data) {
79 np_free(this_monitoring_plugin->state->state_data->data);
80 np_free(this_monitoring_plugin->state->state_data);
81 }
82 np_free(this_monitoring_plugin->state->name);
83 np_free(this_monitoring_plugin->state);
84 }
85 np_free(this_monitoring_plugin->plugin_name); 77 np_free(this_monitoring_plugin->plugin_name);
86 np_free(this_monitoring_plugin); 78 np_free(this_monitoring_plugin);
87 } 79 }
@@ -153,7 +145,8 @@ range *parse_range_string(char *str) {
153 set_range_end(temp_range, end); 145 set_range_end(temp_range, end);
154 } 146 }
155 147
156 if (temp_range->start_infinity == true || temp_range->end_infinity == true || temp_range->start <= temp_range->end) { 148 if (temp_range->start_infinity || temp_range->end_infinity ||
149 temp_range->start <= temp_range->end) {
157 return temp_range; 150 return temp_range;
158 } 151 }
159 free(temp_range); 152 free(temp_range);
@@ -205,12 +198,14 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
205 printf("Threshold not set"); 198 printf("Threshold not set");
206 } else { 199 } else {
207 if (my_threshold->warning) { 200 if (my_threshold->warning) {
208 printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end); 201 printf("Warning: start=%g end=%g; ", my_threshold->warning->start,
202 my_threshold->warning->end);
209 } else { 203 } else {
210 printf("Warning not set; "); 204 printf("Warning not set; ");
211 } 205 }
212 if (my_threshold->critical) { 206 if (my_threshold->critical) {
213 printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end); 207 printf("Critical: start=%g end=%g", my_threshold->critical->start,
208 my_threshold->critical->end);
214 } else { 209 } else {
215 printf("Critical not set"); 210 printf("Critical not set");
216 } 211 }
@@ -222,15 +217,16 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
222bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { 217bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
223 bool is_inside = false; 218 bool is_inside = false;
224 219
225 if (my_range.end_infinity == false && my_range.start_infinity == false) { 220 if (!my_range.end_infinity && !my_range.start_infinity) {
226 // range: .........|---inside---|........... 221 // range: .........|---inside---|...........
227 // value 222 // value
228 is_inside = ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)); 223 is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) &&
229 } else if (my_range.start_infinity == false && my_range.end_infinity == true) { 224 (cmp_perfdata_value(value, my_range.end) <= 0));
225 } else if (!my_range.start_infinity && my_range.end_infinity) {
230 // range: .........|---inside--------- 226 // range: .........|---inside---------
231 // value 227 // value
232 is_inside = (cmp_perfdata_value(my_range.start, value) < 0); 228 is_inside = (cmp_perfdata_value(value, my_range.start) >= 0);
233 } else if (my_range.start_infinity == true && my_range.end_infinity == false) { 229 } else if (my_range.start_infinity && !my_range.end_infinity) {
234 // range: -inside--------|.................... 230 // range: -inside--------|....................
235 // value 231 // value
236 is_inside = (cmp_perfdata_value(value, my_range.end) == -1); 232 is_inside = (cmp_perfdata_value(value, my_range.end) == -1);
@@ -239,7 +235,8 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
239 is_inside = true; 235 is_inside = true;
240 } 236 }
241 237
242 if ((is_inside && my_range.alert_on_inside_range == INSIDE) || (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) { 238 if ((is_inside && my_range.alert_on_inside_range == INSIDE) ||
239 (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) {
243 return true; 240 return true;
244 } 241 }
245 242
@@ -256,21 +253,21 @@ bool check_range(double value, range *my_range) {
256 yes = false; 253 yes = false;
257 } 254 }
258 255
259 if (my_range->end_infinity == false && my_range->start_infinity == false) { 256 if (!my_range->end_infinity && !my_range->start_infinity) {
260 if ((my_range->start <= value) && (value <= my_range->end)) { 257 if ((my_range->start <= value) && (value <= my_range->end)) {
261 return no; 258 return no;
262 } 259 }
263 return yes; 260 return yes;
264 } 261 }
265 262
266 if (my_range->start_infinity == false && my_range->end_infinity == true) { 263 if (!my_range->start_infinity && my_range->end_infinity) {
267 if (my_range->start <= value) { 264 if (my_range->start <= value) {
268 return no; 265 return no;
269 } 266 }
270 return yes; 267 return yes;
271 } 268 }
272 269
273 if (my_range->start_infinity == true && my_range->end_infinity == false) { 270 if (my_range->start_infinity && !my_range->end_infinity) {
274 if (value <= my_range->end) { 271 if (value <= my_range->end) {
275 return no; 272 return no;
276 } 273 }
@@ -282,12 +279,12 @@ bool check_range(double value, range *my_range) {
282/* Returns status */ 279/* Returns status */
283int get_status(double value, thresholds *my_thresholds) { 280int get_status(double value, thresholds *my_thresholds) {
284 if (my_thresholds->critical != NULL) { 281 if (my_thresholds->critical != NULL) {
285 if (check_range(value, my_thresholds->critical) == true) { 282 if (check_range(value, my_thresholds->critical)) {
286 return STATE_CRITICAL; 283 return STATE_CRITICAL;
287 } 284 }
288 } 285 }
289 if (my_thresholds->warning != NULL) { 286 if (my_thresholds->warning != NULL) {
290 if (check_range(value, my_thresholds->warning) == true) { 287 if (check_range(value, my_thresholds->warning)) {
291 return STATE_WARNING; 288 return STATE_WARNING;
292 } 289 }
293 } 290 }
@@ -296,32 +293,31 @@ int get_status(double value, thresholds *my_thresholds) {
296 293
297char *np_escaped_string(const char *string) { 294char *np_escaped_string(const char *string) {
298 char *data; 295 char *data;
299 int i; 296 int write_index = 0;
300 int j = 0;
301 data = strdup(string); 297 data = strdup(string);
302 for (i = 0; data[i]; i++) { 298 for (int i = 0; data[i]; i++) {
303 if (data[i] == '\\') { 299 if (data[i] == '\\') {
304 switch (data[++i]) { 300 switch (data[++i]) {
305 case 'n': 301 case 'n':
306 data[j++] = '\n'; 302 data[write_index++] = '\n';
307 break; 303 break;
308 case 'r': 304 case 'r':
309 data[j++] = '\r'; 305 data[write_index++] = '\r';
310 break; 306 break;
311 case 't': 307 case 't':
312 data[j++] = '\t'; 308 data[write_index++] = '\t';
313 break; 309 break;
314 case '\\': 310 case '\\':
315 data[j++] = '\\'; 311 data[write_index++] = '\\';
316 break; 312 break;
317 default: 313 default:
318 data[j++] = data[i]; 314 data[write_index++] = data[i];
319 } 315 }
320 } else { 316 } else {
321 data[j++] = data[i]; 317 data[write_index++] = data[i];
322 } 318 }
323 } 319 }
324 data[j] = '\0'; 320 data[write_index] = '\0';
325 return data; 321 return data;
326} 322}
327 323
@@ -336,33 +332,35 @@ int np_check_if_root(void) { return (geteuid() == 0); }
336char *np_extract_value(const char *varlist, const char *name, char sep) { 332char *np_extract_value(const char *varlist, const char *name, char sep) {
337 char *tmp = NULL; 333 char *tmp = NULL;
338 char *value = NULL; 334 char *value = NULL;
339 int i;
340 335
341 while (1) { 336 while (true) {
342 /* Strip any leading space */ 337 /* Strip any leading space */
343 for (; isspace(varlist[0]); varlist++) 338 for (; isspace(varlist[0]); varlist++) {
344 ; 339 ;
340 }
345 341
346 if (strncmp(name, varlist, strlen(name)) == 0) { 342 if (strncmp(name, varlist, strlen(name)) == 0) {
347 varlist += strlen(name); 343 varlist += strlen(name);
348 /* strip trailing spaces */ 344 /* strip trailing spaces */
349 for (; isspace(varlist[0]); varlist++) 345 for (; isspace(varlist[0]); varlist++) {
350 ; 346 ;
347 }
351 348
352 if (varlist[0] == '=') { 349 if (varlist[0] == '=') {
353 /* We matched the key, go past the = sign */ 350 /* We matched the key, go past the = sign */
354 varlist++; 351 varlist++;
355 /* strip leading spaces */ 352 /* strip leading spaces */
356 for (; isspace(varlist[0]); varlist++) 353 for (; isspace(varlist[0]); varlist++) {
357 ; 354 ;
355 }
358 356
359 if ((tmp = index(varlist, sep))) { 357 if ((tmp = index(varlist, sep))) {
360 /* Value is delimited by a comma */ 358 /* Value is delimited by a comma */
361 if (tmp - varlist == 0) { 359 if (tmp - varlist == 0) {
362 continue; 360 continue;
363 } 361 }
364 value = (char *)calloc(1, tmp - varlist + 1); 362 value = (char *)calloc(1, (unsigned long)(tmp - varlist + 1));
365 strncpy(value, varlist, tmp - varlist); 363 strncpy(value, varlist, (unsigned long)(tmp - varlist));
366 value[tmp - varlist] = '\0'; 364 value[tmp - varlist] = '\0';
367 } else { 365 } else {
368 /* Value is delimited by a \0 */ 366 /* Value is delimited by a \0 */
@@ -387,7 +385,7 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
387 385
388 /* Clean-up trailing spaces/newlines */ 386 /* Clean-up trailing spaces/newlines */
389 if (value) { 387 if (value) {
390 for (i = strlen(value) - 1; isspace(value[i]); i--) { 388 for (unsigned long i = strlen(value) - 1; isspace(value[i]); i--) {
391 value[i] = '\0'; 389 value[i] = '\0';
392 } 390 }
393 } 391 }
@@ -429,349 +427,3 @@ int mp_translate_state(char *state_text) {
429 } 427 }
430 return ERROR; 428 return ERROR;
431} 429}
432
433/*
434 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
435 * hopefully a unique key per service/plugin invocation. Use the extra-opts
436 * parse of argv, so that uniqueness in parameters are reflected there.
437 */
438char *_np_state_generate_key(void) {
439 int i;
440 char **argv = this_monitoring_plugin->argv;
441 char keyname[41];
442 char *p = NULL;
443
444 unsigned char result[256];
445
446#ifdef USE_OPENSSL
447 /*
448 * This code path is chosen if openssl is available (which should be the most common
449 * scenario). Alternatively, the gnulib implementation/
450 *
451 */
452 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
453
454 EVP_DigestInit(ctx, EVP_sha256());
455
456 for (i = 0; i < this_monitoring_plugin->argc; i++) {
457 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
458 }
459
460 EVP_DigestFinal(ctx, result, NULL);
461#else
462
463 struct sha256_ctx ctx;
464
465 for (i = 0; i < this_monitoring_plugin->argc; i++) {
466 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
467 }
468
469 sha256_finish_ctx(&ctx, result);
470#endif // FOUNDOPENSSL
471
472 for (i = 0; i < 20; ++i) {
473 sprintf(&keyname[2 * i], "%02x", result[i]);
474 }
475
476 keyname[40] = '\0';
477
478 p = strdup(keyname);
479 if (p == NULL) {
480 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
481 }
482 return p;
483}
484
485void _cleanup_state_data(void) {
486 if (this_monitoring_plugin->state->state_data != NULL) {
487 np_free(this_monitoring_plugin->state->state_data->data);
488 np_free(this_monitoring_plugin->state->state_data);
489 }
490}
491
492/*
493 * Internal function. Returns either:
494 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
495 * statically compiled shared state directory
496 */
497char *_np_state_calculate_location_prefix(void) {
498 char *env_dir;
499
500 /* Do not allow passing MP_STATE_PATH in setuid plugins
501 * for security reasons */
502 if (!mp_suid()) {
503 env_dir = getenv("MP_STATE_PATH");
504 if (env_dir && env_dir[0] != '\0') {
505 return env_dir;
506 }
507 /* This is the former ENV, for backward-compatibility */
508 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
509 if (env_dir && env_dir[0] != '\0') {
510 return env_dir;
511 }
512 }
513
514 return NP_STATE_DIR_PREFIX;
515}
516
517/*
518 * Initiatializer for state routines.
519 * Sets variables. Generates filename. Returns np_state_key. die with
520 * UNKNOWN if exception
521 */
522void np_enable_state(char *keyname, int expected_data_version) {
523 state_key *this_state = NULL;
524 char *temp_filename = NULL;
525 char *temp_keyname = NULL;
526 char *p = NULL;
527 int ret;
528
529 if (this_monitoring_plugin == NULL) {
530 die(STATE_UNKNOWN, _("This requires np_init to be called"));
531 }
532
533 this_state = (state_key *)calloc(1, sizeof(state_key));
534 if (this_state == NULL) {
535 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
536 }
537
538 if (keyname == NULL) {
539 temp_keyname = _np_state_generate_key();
540 } else {
541 temp_keyname = strdup(keyname);
542 if (temp_keyname == NULL) {
543 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
544 }
545 }
546 /* Die if invalid characters used for keyname */
547 p = temp_keyname;
548 while (*p != '\0') {
549 if (!(isalnum(*p) || *p == '_')) {
550 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
551 }
552 p++;
553 }
554 this_state->name = temp_keyname;
555 this_state->plugin_name = this_monitoring_plugin->plugin_name;
556 this_state->data_version = expected_data_version;
557 this_state->state_data = NULL;
558
559 /* Calculate filename */
560 ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
561 this_monitoring_plugin->plugin_name, this_state->name);
562 if (ret < 0) {
563 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
564 }
565
566 this_state->_filename = temp_filename;
567
568 this_monitoring_plugin->state = this_state;
569}
570
571/*
572 * Will return NULL if no data is available (first run). If key currently
573 * exists, read data. If state file format version is not expected, return
574 * as if no data. Get state data version number and compares to expected.
575 * If numerically lower, then return as no previous state. die with UNKNOWN
576 * if exceptional error.
577 */
578state_data *np_state_read(void) {
579 state_data *this_state_data = NULL;
580 FILE *statefile;
581 bool rc = false;
582
583 if (this_monitoring_plugin == NULL) {
584 die(STATE_UNKNOWN, _("This requires np_init to be called"));
585 }
586
587 /* Open file. If this fails, no previous state found */
588 statefile = fopen(this_monitoring_plugin->state->_filename, "r");
589 if (statefile != NULL) {
590
591 this_state_data = (state_data *)calloc(1, sizeof(state_data));
592 if (this_state_data == NULL) {
593 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
594 }
595
596 this_state_data->data = NULL;
597 this_monitoring_plugin->state->state_data = this_state_data;
598
599 rc = _np_state_read_file(statefile);
600
601 fclose(statefile);
602 }
603
604 if (!rc) {
605 _cleanup_state_data();
606 }
607
608 return this_monitoring_plugin->state->state_data;
609}
610
611/*
612 * Read the state file
613 */
614bool _np_state_read_file(FILE *f) {
615 bool status = false;
616 size_t pos;
617 char *line;
618 int i;
619 int failure = 0;
620 time_t current_time, data_time;
621 enum {
622 STATE_FILE_VERSION,
623 STATE_DATA_VERSION,
624 STATE_DATA_TIME,
625 STATE_DATA_TEXT,
626 STATE_DATA_END
627 } expected = STATE_FILE_VERSION;
628
629 time(&current_time);
630
631 /* Note: This introduces a limit of 1024 bytes in the string data */
632 line = (char *)calloc(1, 1024);
633 if (line == NULL) {
634 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
635 }
636
637 while (!failure && (fgets(line, 1024, f)) != NULL) {
638 pos = strlen(line);
639 if (line[pos - 1] == '\n') {
640 line[pos - 1] = '\0';
641 }
642
643 if (line[0] == '#') {
644 continue;
645 }
646
647 switch (expected) {
648 case STATE_FILE_VERSION:
649 i = atoi(line);
650 if (i != NP_STATE_FORMAT_VERSION) {
651 failure++;
652 } else {
653 expected = STATE_DATA_VERSION;
654 }
655 break;
656 case STATE_DATA_VERSION:
657 i = atoi(line);
658 if (i != this_monitoring_plugin->state->data_version) {
659 failure++;
660 } else {
661 expected = STATE_DATA_TIME;
662 }
663 break;
664 case STATE_DATA_TIME:
665 /* If time > now, error */
666 data_time = strtoul(line, NULL, 10);
667 if (data_time > current_time) {
668 failure++;
669 } else {
670 this_monitoring_plugin->state->state_data->time = data_time;
671 expected = STATE_DATA_TEXT;
672 }
673 break;
674 case STATE_DATA_TEXT:
675 this_monitoring_plugin->state->state_data->data = strdup(line);
676 if (this_monitoring_plugin->state->state_data->data == NULL) {
677 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
678 }
679 expected = STATE_DATA_END;
680 status = true;
681 break;
682 case STATE_DATA_END:;
683 }
684 }
685
686 np_free(line);
687 return status;
688}
689
690/*
691 * If time=NULL, use current time. Create state file, with state format
692 * version, default text. Writes version, time, and data. Avoid locking
693 * problems - use mv to write and then swap. Possible loss of state data if
694 * two things writing to same key at same time.
695 * Will die with UNKNOWN if errors
696 */
697void np_state_write_string(time_t data_time, char *data_string) {
698 FILE *fp;
699 char *temp_file = NULL;
700 int fd = 0, result = 0;
701 time_t current_time;
702 char *directories = NULL;
703 char *p = NULL;
704
705 if (data_time == 0) {
706 time(&current_time);
707 } else {
708 current_time = data_time;
709 }
710
711 /* If file doesn't currently exist, create directories */
712 if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) {
713 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
714 if (result < 0) {
715 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
716 }
717
718 for (p = directories + 1; *p; p++) {
719 if (*p == '/') {
720 *p = '\0';
721 if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) {
722 /* Can't free this! Otherwise error message is wrong! */
723 /* np_free(directories); */
724 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
725 }
726 *p = '/';
727 }
728 }
729 np_free(directories);
730 }
731
732 result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename);
733 if (result < 0) {
734 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
735 }
736
737 if ((fd = mkstemp(temp_file)) == -1) {
738 np_free(temp_file);
739 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
740 }
741
742 fp = (FILE *)fdopen(fd, "w");
743 if (fp == NULL) {
744 close(fd);
745 unlink(temp_file);
746 np_free(temp_file);
747 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
748 }
749
750 fprintf(fp, "# NP State file\n");
751 fprintf(fp, "%d\n", NP_STATE_FORMAT_VERSION);
752 fprintf(fp, "%d\n", this_monitoring_plugin->state->data_version);
753 fprintf(fp, "%lu\n", current_time);
754 fprintf(fp, "%s\n", data_string);
755
756 fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
757
758 fflush(fp);
759
760 result = fclose(fp);
761
762 fsync(fd);
763
764 if (result != 0) {
765 unlink(temp_file);
766 np_free(temp_file);
767 die(STATE_UNKNOWN, _("Error writing temp file"));
768 }
769
770 if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) {
771 unlink(temp_file);
772 np_free(temp_file);
773 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
774 }
775
776 np_free(temp_file);
777}