CI: Fallback on mysql service
[monitoring-plugins.git] / lib / utils_base.c
blobc458cf61aa0df68e6497c648a4e0aa05b479b2bb
1 /*****************************************************************************
3 * utils_base.c
5 * License: GPL
6 * Copyright (c) 2006 Monitoring Plugins Development Team
8 * Library of useful functions for plugins
9 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *****************************************************************************/
27 #include "../plugins/common.h"
28 #include <stdarg.h>
29 #include "utils_base.h"
30 #include <ctype.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <sys/types.h>
36 #define np_free(ptr) { if(ptr) { free(ptr); ptr = NULL; } }
38 monitoring_plugin *this_monitoring_plugin=NULL;
40 unsigned int timeout_state = STATE_CRITICAL;
41 unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
43 int _np_state_read_file(FILE *);
45 void np_init( char *plugin_name, int argc, char **argv ) {
46 if (this_monitoring_plugin==NULL) {
47 this_monitoring_plugin = calloc(1, sizeof(monitoring_plugin));
48 if (this_monitoring_plugin==NULL) {
49 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
50 strerror(errno));
52 this_monitoring_plugin->plugin_name = strdup(plugin_name);
53 if (this_monitoring_plugin->plugin_name==NULL)
54 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
55 this_monitoring_plugin->argc = argc;
56 this_monitoring_plugin->argv = argv;
60 void np_set_args( int argc, char **argv ) {
61 if (this_monitoring_plugin==NULL)
62 die(STATE_UNKNOWN, _("This requires np_init to be called"));
64 this_monitoring_plugin->argc = argc;
65 this_monitoring_plugin->argv = argv;
69 void np_cleanup() {
70 if (this_monitoring_plugin!=NULL) {
71 if(this_monitoring_plugin->state!=NULL) {
72 if(this_monitoring_plugin->state->state_data) {
73 np_free(this_monitoring_plugin->state->state_data->data);
74 np_free(this_monitoring_plugin->state->state_data);
76 np_free(this_monitoring_plugin->state->name);
77 np_free(this_monitoring_plugin->state);
79 np_free(this_monitoring_plugin->plugin_name);
80 np_free(this_monitoring_plugin);
82 this_monitoring_plugin=NULL;
85 /* Hidden function to get a pointer to this_monitoring_plugin for testing */
86 void _get_monitoring_plugin( monitoring_plugin **pointer ){
87 *pointer = this_monitoring_plugin;
90 void
91 die (int result, const char *fmt, ...)
93 if(fmt!=NULL) {
94 va_list ap;
95 va_start (ap, fmt);
96 vprintf (fmt, ap);
97 va_end (ap);
100 if(this_monitoring_plugin!=NULL) {
101 np_cleanup();
103 exit (result);
106 void set_range_start (range *this, double value) {
107 this->start = value;
108 this->start_infinity = FALSE;
111 void set_range_end (range *this, double value) {
112 this->end = value;
113 this->end_infinity = FALSE;
116 range
117 *parse_range_string (char *str) {
118 range *temp_range;
119 double start;
120 double end;
121 char *end_str;
123 temp_range = (range *) calloc(1, sizeof(range));
125 /* Set defaults */
126 temp_range->start = 0;
127 temp_range->start_infinity = FALSE;
128 temp_range->end = 0;
129 temp_range->end_infinity = TRUE;
130 temp_range->alert_on = OUTSIDE;
131 temp_range->text = strdup(str);
133 if (str[0] == '@') {
134 temp_range->alert_on = INSIDE;
135 str++;
138 end_str = index(str, ':');
139 if (end_str != NULL) {
140 if (str[0] == '~') {
141 temp_range->start_infinity = TRUE;
142 } else {
143 start = strtod(str, NULL); /* Will stop at the ':' */
144 set_range_start(temp_range, start);
146 end_str++; /* Move past the ':' */
147 } else {
148 end_str = str;
150 end = strtod(end_str, NULL);
151 if (strcmp(end_str, "") != 0) {
152 set_range_end(temp_range, end);
155 if (temp_range->start_infinity == TRUE ||
156 temp_range->end_infinity == TRUE ||
157 temp_range->start <= temp_range->end) {
158 return temp_range;
160 free(temp_range);
161 return NULL;
164 /* returns 0 if okay, otherwise 1 */
166 _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
168 thresholds *temp_thresholds = NULL;
170 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL)
171 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
172 strerror(errno));
174 temp_thresholds->warning = NULL;
175 temp_thresholds->critical = NULL;
177 if (warn_string != NULL) {
178 if ((temp_thresholds->warning = parse_range_string(warn_string)) == NULL) {
179 return NP_RANGE_UNPARSEABLE;
182 if (critical_string != NULL) {
183 if ((temp_thresholds->critical = parse_range_string(critical_string)) == NULL) {
184 return NP_RANGE_UNPARSEABLE;
188 *my_thresholds = temp_thresholds;
190 return 0;
193 void
194 set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
196 switch (_set_thresholds(my_thresholds, warn_string, critical_string)) {
197 case 0:
198 return;
199 case NP_RANGE_UNPARSEABLE:
200 die(STATE_UNKNOWN, _("Range format incorrect"));
201 case NP_WARN_WITHIN_CRIT:
202 die(STATE_UNKNOWN, _("Warning level is a subset of critical and will not be alerted"));
203 break;
207 void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
208 printf("%s - ", threshold_name);
209 if (! my_threshold) {
210 printf("Threshold not set");
211 } else {
212 if (my_threshold->warning) {
213 printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end);
214 } else {
215 printf("Warning not set; ");
217 if (my_threshold->critical) {
218 printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end);
219 } else {
220 printf("Critical not set");
223 printf("\n");
226 /* Returns TRUE if alert should be raised based on the range */
228 check_range(double value, range *my_range)
230 int no = FALSE;
231 int yes = TRUE;
233 if (my_range->alert_on == INSIDE) {
234 no = TRUE;
235 yes = FALSE;
238 if (my_range->end_infinity == FALSE && my_range->start_infinity == FALSE) {
239 if ((my_range->start <= value) && (value <= my_range->end)) {
240 return no;
241 } else {
242 return yes;
244 } else if (my_range->start_infinity == FALSE && my_range->end_infinity == TRUE) {
245 if (my_range->start <= value) {
246 return no;
247 } else {
248 return yes;
250 } else if (my_range->start_infinity == TRUE && my_range->end_infinity == FALSE) {
251 if (value <= my_range->end) {
252 return no;
253 } else {
254 return yes;
256 } else {
257 return no;
261 /* Returns status */
263 get_status(double value, thresholds *my_thresholds)
265 if (my_thresholds->critical != NULL) {
266 if (check_range(value, my_thresholds->critical) == TRUE) {
267 return STATE_CRITICAL;
270 if (my_thresholds->warning != NULL) {
271 if (check_range(value, my_thresholds->warning) == TRUE) {
272 return STATE_WARNING;
275 return STATE_OK;
278 char *np_escaped_string (const char *string) {
279 char *data;
280 int i, j=0;
281 data = strdup(string);
282 for (i=0; data[i]; i++) {
283 if (data[i] == '\\') {
284 switch(data[++i]) {
285 case 'n':
286 data[j++] = '\n';
287 break;
288 case 'r':
289 data[j++] = '\r';
290 break;
291 case 't':
292 data[j++] = '\t';
293 break;
294 case '\\':
295 data[j++] = '\\';
296 break;
297 default:
298 data[j++] = data[i];
300 } else {
301 data[j++] = data[i];
304 data[j] = '\0';
305 return data;
308 int np_check_if_root(void) { return (geteuid() == 0); }
311 * Extract the value from key/value pairs, or return NULL. The value returned
312 * can be free()ed.
313 * This function can be used to parse NTP control packet data and performance
314 * data strings.
316 char *np_extract_value(const char *varlist, const char *name, char sep) {
317 char *tmp=NULL, *value=NULL;
318 int i;
320 while (1) {
321 /* Strip any leading space */
322 for (; isspace(varlist[0]); varlist++);
324 if (strncmp(name, varlist, strlen(name)) == 0) {
325 varlist += strlen(name);
326 /* strip trailing spaces */
327 for (; isspace(varlist[0]); varlist++);
329 if (varlist[0] == '=') {
330 /* We matched the key, go past the = sign */
331 varlist++;
332 /* strip leading spaces */
333 for (; isspace(varlist[0]); varlist++);
335 if (tmp = index(varlist, sep)) {
336 /* Value is delimited by a comma */
337 if (tmp-varlist == 0) continue;
338 value = (char *)calloc(1, tmp-varlist+1);
339 strncpy(value, varlist, tmp-varlist);
340 value[tmp-varlist] = '\0';
341 } else {
342 /* Value is delimited by a \0 */
343 if (strlen(varlist) == 0) continue;
344 value = (char *)calloc(1, strlen(varlist) + 1);
345 strncpy(value, varlist, strlen(varlist));
346 value[strlen(varlist)] = '\0';
348 break;
351 if (tmp = index(varlist, sep)) {
352 /* More keys, keep going... */
353 varlist = tmp + 1;
354 } else {
355 /* We're done */
356 break;
360 /* Clean-up trailing spaces/newlines */
361 if (value) for (i=strlen(value)-1; isspace(value[i]); i--) value[i] = '\0';
363 return value;
366 const char *
367 state_text (int result)
369 switch (result) {
370 case STATE_OK:
371 return "OK";
372 case STATE_WARNING:
373 return "WARNING";
374 case STATE_CRITICAL:
375 return "CRITICAL";
376 case STATE_DEPENDENT:
377 return "DEPENDENT";
378 default:
379 return "UNKNOWN";
384 * Read a string representing a state (ok, warning... or numeric: 0, 1) and
385 * return the corresponding STATE_ value or ERROR)
387 int mp_translate_state (char *state_text) {
388 if (!strcasecmp(state_text,"OK") || !strcmp(state_text,"0"))
389 return STATE_OK;
390 if (!strcasecmp(state_text,"WARNING") || !strcmp(state_text,"1"))
391 return STATE_WARNING;
392 if (!strcasecmp(state_text,"CRITICAL") || !strcmp(state_text,"2"))
393 return STATE_CRITICAL;
394 if (!strcasecmp(state_text,"UNKNOWN") || !strcmp(state_text,"3"))
395 return STATE_UNKNOWN;
396 return ERROR;
400 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
401 * hopefully a unique key per service/plugin invocation. Use the extra-opts
402 * parse of argv, so that uniqueness in parameters are reflected there.
404 char *_np_state_generate_key() {
405 struct sha256_ctx ctx;
406 int i;
407 char **argv = this_monitoring_plugin->argv;
408 unsigned char result[20];
409 char keyname[41];
410 char *p=NULL;
412 sha256_init_ctx(&ctx);
414 for(i=0; i<this_monitoring_plugin->argc; i++) {
415 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
418 sha256_finish_ctx(&ctx, &result);
420 for (i=0; i<20; ++i) {
421 sprintf(&keyname[2*i], "%02x", result[i]);
423 keyname[40]='\0';
425 p = strdup(keyname);
426 if(p==NULL) {
427 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
429 return p;
432 void _cleanup_state_data() {
433 if (this_monitoring_plugin->state->state_data!=NULL) {
434 np_free(this_monitoring_plugin->state->state_data->data);
435 np_free(this_monitoring_plugin->state->state_data);
440 * Internal function. Returns either:
441 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
442 * statically compiled shared state directory
444 char* _np_state_calculate_location_prefix(){
445 char *env_dir;
447 /* Do not allow passing MP_STATE_PATH in setuid plugins
448 * for security reasons */
449 if (mp_suid() == FALSE) {
450 env_dir = getenv("MP_STATE_PATH");
451 if(env_dir && env_dir[0] != '\0')
452 return env_dir;
453 /* This is the former ENV, for backward-compatibility */
454 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
455 if(env_dir && env_dir[0] != '\0')
456 return env_dir;
459 return NP_STATE_DIR_PREFIX;
463 * Initiatializer for state routines.
464 * Sets variables. Generates filename. Returns np_state_key. die with
465 * UNKNOWN if exception
467 void np_enable_state(char *keyname, int expected_data_version) {
468 state_key *this_state = NULL;
469 char *temp_filename = NULL;
470 char *temp_keyname = NULL;
471 char *p=NULL;
472 int ret;
474 if(this_monitoring_plugin==NULL)
475 die(STATE_UNKNOWN, _("This requires np_init to be called"));
477 this_state = (state_key *) calloc(1, sizeof(state_key));
478 if(this_state==NULL)
479 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
480 strerror(errno));
482 if(keyname==NULL) {
483 temp_keyname = _np_state_generate_key();
484 } else {
485 temp_keyname = strdup(keyname);
486 if(temp_keyname==NULL)
487 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
489 /* Die if invalid characters used for keyname */
490 p = temp_keyname;
491 while(*p!='\0') {
492 if(! (isalnum(*p) || *p == '_')) {
493 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
495 p++;
497 this_state->name=temp_keyname;
498 this_state->plugin_name=this_monitoring_plugin->plugin_name;
499 this_state->data_version=expected_data_version;
500 this_state->state_data=NULL;
502 /* Calculate filename */
503 ret = asprintf(&temp_filename, "%s/%lu/%s/%s",
504 _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
505 this_monitoring_plugin->plugin_name, this_state->name);
506 if (ret < 0)
507 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
508 strerror(errno));
510 this_state->_filename=temp_filename;
512 this_monitoring_plugin->state = this_state;
516 * Will return NULL if no data is available (first run). If key currently
517 * exists, read data. If state file format version is not expected, return
518 * as if no data. Get state data version number and compares to expected.
519 * If numerically lower, then return as no previous state. die with UNKNOWN
520 * if exceptional error.
522 state_data *np_state_read() {
523 state_data *this_state_data=NULL;
524 FILE *statefile;
525 int rc = FALSE;
527 if(this_monitoring_plugin==NULL)
528 die(STATE_UNKNOWN, _("This requires np_init to be called"));
530 /* Open file. If this fails, no previous state found */
531 statefile = fopen( this_monitoring_plugin->state->_filename, "r" );
532 if(statefile!=NULL) {
534 this_state_data = (state_data *) calloc(1, sizeof(state_data));
535 if(this_state_data==NULL)
536 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
537 strerror(errno));
539 this_state_data->data=NULL;
540 this_monitoring_plugin->state->state_data = this_state_data;
542 rc = _np_state_read_file(statefile);
544 fclose(statefile);
547 if(rc==FALSE) {
548 _cleanup_state_data();
551 return this_monitoring_plugin->state->state_data;
555 * Read the state file
557 int _np_state_read_file(FILE *f) {
558 int status=FALSE;
559 size_t pos;
560 char *line;
561 int i;
562 int failure=0;
563 time_t current_time, data_time;
564 enum { STATE_FILE_VERSION, STATE_DATA_VERSION, STATE_DATA_TIME, STATE_DATA_TEXT, STATE_DATA_END } expected=STATE_FILE_VERSION;
566 time(&current_time);
568 /* Note: This introduces a limit of 1024 bytes in the string data */
569 line = (char *) calloc(1, 1024);
570 if(line==NULL)
571 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
572 strerror(errno));
574 while(!failure && (fgets(line,1024,f))!=NULL){
575 pos=strlen(line);
576 if(line[pos-1]=='\n') {
577 line[pos-1]='\0';
580 if(line[0] == '#') continue;
582 switch(expected) {
583 case STATE_FILE_VERSION:
584 i=atoi(line);
585 if(i!=NP_STATE_FORMAT_VERSION)
586 failure++;
587 else
588 expected=STATE_DATA_VERSION;
589 break;
590 case STATE_DATA_VERSION:
591 i=atoi(line);
592 if(i != this_monitoring_plugin->state->data_version)
593 failure++;
594 else
595 expected=STATE_DATA_TIME;
596 break;
597 case STATE_DATA_TIME:
598 /* If time > now, error */
599 data_time=strtoul(line,NULL,10);
600 if(data_time > current_time)
601 failure++;
602 else {
603 this_monitoring_plugin->state->state_data->time = data_time;
604 expected=STATE_DATA_TEXT;
606 break;
607 case STATE_DATA_TEXT:
608 this_monitoring_plugin->state->state_data->data = strdup(line);
609 if(this_monitoring_plugin->state->state_data->data==NULL)
610 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
611 expected=STATE_DATA_END;
612 status=TRUE;
613 break;
614 case STATE_DATA_END:
619 np_free(line);
620 return status;
624 * If time=NULL, use current time. Create state file, with state format
625 * version, default text. Writes version, time, and data. Avoid locking
626 * problems - use mv to write and then swap. Possible loss of state data if
627 * two things writing to same key at same time.
628 * Will die with UNKNOWN if errors
630 void np_state_write_string(time_t data_time, char *data_string) {
631 FILE *fp;
632 char *temp_file=NULL;
633 int fd=0, result=0;
634 time_t current_time;
635 char *directories=NULL;
636 char *p=NULL;
638 if(data_time==0)
639 time(&current_time);
640 else
641 current_time=data_time;
643 /* If file doesn't currently exist, create directories */
644 if(access(this_monitoring_plugin->state->_filename,F_OK)!=0) {
645 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
646 if(result < 0)
647 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
648 strerror(errno));
650 for(p=directories+1; *p; p++) {
651 if(*p=='/') {
652 *p='\0';
653 if((access(directories,F_OK)!=0) && (mkdir(directories, S_IRWXU)!=0)) {
654 /* Can't free this! Otherwise error message is wrong! */
655 /* np_free(directories); */
656 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
658 *p='/';
661 np_free(directories);
664 result = asprintf(&temp_file,"%s.XXXXXX",this_monitoring_plugin->state->_filename);
665 if(result < 0)
666 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
667 strerror(errno));
669 if((fd=mkstemp(temp_file))==-1) {
670 np_free(temp_file);
671 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
674 fp=(FILE *)fdopen(fd,"w");
675 if(fp==NULL) {
676 close(fd);
677 unlink(temp_file);
678 np_free(temp_file);
679 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
682 fprintf(fp,"# NP State file\n");
683 fprintf(fp,"%d\n",NP_STATE_FORMAT_VERSION);
684 fprintf(fp,"%d\n",this_monitoring_plugin->state->data_version);
685 fprintf(fp,"%lu\n",current_time);
686 fprintf(fp,"%s\n",data_string);
688 fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
690 fflush(fp);
692 result=fclose(fp);
694 fsync(fd);
696 if(result!=0) {
697 unlink(temp_file);
698 np_free(temp_file);
699 die(STATE_UNKNOWN, _("Error writing temp file"));
702 if(rename(temp_file, this_monitoring_plugin->state->_filename)!=0) {
703 unlink(temp_file);
704 np_free(temp_file);
705 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
708 np_free(temp_file);