Avoid passing NULL to vprintf().
[monitoring-plugins.git] / lib / utils_base.c
blob19a531f5bf6cec30173ffd2bd2d9177880ddd7a1
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 "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 int _np_state_read_file(FILE *);
42 void np_init( char *plugin_name, int argc, char **argv ) {
43 if (this_monitoring_plugin==NULL) {
44 this_monitoring_plugin = calloc(1, sizeof(monitoring_plugin));
45 if (this_monitoring_plugin==NULL) {
46 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
47 strerror(errno));
49 this_monitoring_plugin->plugin_name = strdup(plugin_name);
50 if (this_monitoring_plugin->plugin_name==NULL)
51 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
52 this_monitoring_plugin->argc = argc;
53 this_monitoring_plugin->argv = argv;
57 void np_set_args( int argc, char **argv ) {
58 if (this_monitoring_plugin==NULL)
59 die(STATE_UNKNOWN, _("This requires np_init to be called"));
61 this_monitoring_plugin->argc = argc;
62 this_monitoring_plugin->argv = argv;
66 void np_cleanup() {
67 if (this_monitoring_plugin!=NULL) {
68 if(this_monitoring_plugin->state!=NULL) {
69 if(this_monitoring_plugin->state->state_data) {
70 np_free(this_monitoring_plugin->state->state_data->data);
71 np_free(this_monitoring_plugin->state->state_data);
73 np_free(this_monitoring_plugin->state->name);
74 np_free(this_monitoring_plugin->state);
76 np_free(this_monitoring_plugin->plugin_name);
77 np_free(this_monitoring_plugin);
79 this_monitoring_plugin=NULL;
82 /* Hidden function to get a pointer to this_monitoring_plugin for testing */
83 void _get_monitoring_plugin( monitoring_plugin **pointer ){
84 *pointer = this_monitoring_plugin;
87 void
88 die (int result, const char *fmt, ...)
90 if(fmt!=NULL) {
91 va_list ap;
92 va_start (ap, fmt);
93 vprintf (fmt, ap);
94 va_end (ap);
97 if(this_monitoring_plugin!=NULL) {
98 np_cleanup();
100 exit (result);
103 void set_range_start (range *this, double value) {
104 this->start = value;
105 this->start_infinity = FALSE;
108 void set_range_end (range *this, double value) {
109 this->end = value;
110 this->end_infinity = FALSE;
113 range
114 *parse_range_string (char *str) {
115 range *temp_range;
116 double start;
117 double end;
118 char *end_str;
120 temp_range = (range *) calloc(1, sizeof(range));
122 /* Set defaults */
123 temp_range->start = 0;
124 temp_range->start_infinity = FALSE;
125 temp_range->end = 0;
126 temp_range->end_infinity = TRUE;
127 temp_range->alert_on = OUTSIDE;
129 if (str[0] == '@') {
130 temp_range->alert_on = INSIDE;
131 str++;
134 end_str = index(str, ':');
135 if (end_str != NULL) {
136 if (str[0] == '~') {
137 temp_range->start_infinity = TRUE;
138 } else {
139 start = strtod(str, NULL); /* Will stop at the ':' */
140 set_range_start(temp_range, start);
142 end_str++; /* Move past the ':' */
143 } else {
144 end_str = str;
146 end = strtod(end_str, NULL);
147 if (strcmp(end_str, "") != 0) {
148 set_range_end(temp_range, end);
151 if (temp_range->start_infinity == TRUE ||
152 temp_range->end_infinity == TRUE ||
153 temp_range->start <= temp_range->end) {
154 return temp_range;
156 free(temp_range);
157 return NULL;
160 /* returns 0 if okay, otherwise 1 */
162 _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
164 thresholds *temp_thresholds = NULL;
166 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL)
167 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
168 strerror(errno));
170 temp_thresholds->warning = NULL;
171 temp_thresholds->critical = NULL;
173 if (warn_string != NULL) {
174 if ((temp_thresholds->warning = parse_range_string(warn_string)) == NULL) {
175 return NP_RANGE_UNPARSEABLE;
178 if (critical_string != NULL) {
179 if ((temp_thresholds->critical = parse_range_string(critical_string)) == NULL) {
180 return NP_RANGE_UNPARSEABLE;
184 *my_thresholds = temp_thresholds;
186 return 0;
189 void
190 set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
192 switch (_set_thresholds(my_thresholds, warn_string, critical_string)) {
193 case 0:
194 return;
195 case NP_RANGE_UNPARSEABLE:
196 die(STATE_UNKNOWN, _("Range format incorrect"));
197 case NP_WARN_WITHIN_CRIT:
198 die(STATE_UNKNOWN, _("Warning level is a subset of critical and will not be alerted"));
199 break;
203 void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
204 printf("%s - ", threshold_name);
205 if (! my_threshold) {
206 printf("Threshold not set");
207 } else {
208 if (my_threshold->warning) {
209 printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end);
210 } else {
211 printf("Warning not set; ");
213 if (my_threshold->critical) {
214 printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end);
215 } else {
216 printf("Critical not set");
219 printf("\n");
222 /* Returns TRUE if alert should be raised based on the range */
224 check_range(double value, range *my_range)
226 int no = FALSE;
227 int yes = TRUE;
229 if (my_range->alert_on == INSIDE) {
230 no = TRUE;
231 yes = FALSE;
234 if (my_range->end_infinity == FALSE && my_range->start_infinity == FALSE) {
235 if ((my_range->start <= value) && (value <= my_range->end)) {
236 return no;
237 } else {
238 return yes;
240 } else if (my_range->start_infinity == FALSE && my_range->end_infinity == TRUE) {
241 if (my_range->start <= value) {
242 return no;
243 } else {
244 return yes;
246 } else if (my_range->start_infinity == TRUE && my_range->end_infinity == FALSE) {
247 if (value <= my_range->end) {
248 return no;
249 } else {
250 return yes;
252 } else {
253 return no;
257 /* Returns status */
259 get_status(double value, thresholds *my_thresholds)
261 if (my_thresholds->critical != NULL) {
262 if (check_range(value, my_thresholds->critical) == TRUE) {
263 return STATE_CRITICAL;
266 if (my_thresholds->warning != NULL) {
267 if (check_range(value, my_thresholds->warning) == TRUE) {
268 return STATE_WARNING;
271 return STATE_OK;
274 char *np_escaped_string (const char *string) {
275 char *data;
276 int i, j=0;
277 data = strdup(string);
278 for (i=0; data[i]; i++) {
279 if (data[i] == '\\') {
280 switch(data[++i]) {
281 case 'n':
282 data[j++] = '\n';
283 break;
284 case 'r':
285 data[j++] = '\r';
286 break;
287 case 't':
288 data[j++] = '\t';
289 break;
290 case '\\':
291 data[j++] = '\\';
292 break;
293 default:
294 data[j++] = data[i];
296 } else {
297 data[j++] = data[i];
300 data[j] = '\0';
301 return data;
304 int np_check_if_root(void) { return (geteuid() == 0); }
307 * Extract the value from key/value pairs, or return NULL. The value returned
308 * can be free()ed.
309 * This function can be used to parse NTP control packet data and performance
310 * data strings.
312 char *np_extract_value(const char *varlist, const char *name, char sep) {
313 char *tmp=NULL, *value=NULL;
314 int i;
316 while (1) {
317 /* Strip any leading space */
318 for (varlist; isspace(varlist[0]); varlist++);
320 if (strncmp(name, varlist, strlen(name)) == 0) {
321 varlist += strlen(name);
322 /* strip trailing spaces */
323 for (varlist; isspace(varlist[0]); varlist++);
325 if (varlist[0] == '=') {
326 /* We matched the key, go past the = sign */
327 varlist++;
328 /* strip leading spaces */
329 for (varlist; isspace(varlist[0]); varlist++);
331 if (tmp = index(varlist, sep)) {
332 /* Value is delimited by a comma */
333 if (tmp-varlist == 0) continue;
334 value = (char *)calloc(1, tmp-varlist+1);
335 strncpy(value, varlist, tmp-varlist);
336 value[tmp-varlist] = '\0';
337 } else {
338 /* Value is delimited by a \0 */
339 if (strlen(varlist) == 0) continue;
340 value = (char *)calloc(1, strlen(varlist) + 1);
341 strncpy(value, varlist, strlen(varlist));
342 value[strlen(varlist)] = '\0';
344 break;
347 if (tmp = index(varlist, sep)) {
348 /* More keys, keep going... */
349 varlist = tmp + 1;
350 } else {
351 /* We're done */
352 break;
356 /* Clean-up trailing spaces/newlines */
357 if (value) for (i=strlen(value)-1; isspace(value[i]); i--) value[i] = '\0';
359 return value;
364 * Read a string representing a state (ok, warning... or numeric: 0, 1) and
365 * return the corresponding STATE_ value or ERROR)
367 int mp_translate_state (char *state_text) {
368 if (!strcasecmp(state_text,"OK") || !strcmp(state_text,"0"))
369 return STATE_OK;
370 if (!strcasecmp(state_text,"WARNING") || !strcmp(state_text,"1"))
371 return STATE_WARNING;
372 if (!strcasecmp(state_text,"CRITICAL") || !strcmp(state_text,"2"))
373 return STATE_CRITICAL;
374 if (!strcasecmp(state_text,"UNKNOWN") || !strcmp(state_text,"3"))
375 return STATE_UNKNOWN;
376 return ERROR;
380 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
381 * hopefully a unique key per service/plugin invocation. Use the extra-opts
382 * parse of argv, so that uniqueness in parameters are reflected there.
384 char *_np_state_generate_key() {
385 struct sha1_ctx ctx;
386 int i;
387 char **argv = this_monitoring_plugin->argv;
388 unsigned char result[20];
389 char keyname[41];
390 char *p=NULL;
392 sha1_init_ctx(&ctx);
394 for(i=0; i<this_monitoring_plugin->argc; i++) {
395 sha1_process_bytes(argv[i], strlen(argv[i]), &ctx);
398 sha1_finish_ctx(&ctx, &result);
400 for (i=0; i<20; ++i) {
401 sprintf(&keyname[2*i], "%02x", result[i]);
403 keyname[40]='\0';
405 p = strdup(keyname);
406 if(p==NULL) {
407 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
409 return p;
412 void _cleanup_state_data() {
413 if (this_monitoring_plugin->state->state_data!=NULL) {
414 np_free(this_monitoring_plugin->state->state_data->data);
415 np_free(this_monitoring_plugin->state->state_data);
420 * Internal function. Returns either:
421 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
422 * statically compiled shared state directory
424 char* _np_state_calculate_location_prefix(){
425 char *env_dir;
427 /* Do not allow passing MP_STATE_PATH in setuid plugins
428 * for security reasons */
429 if (mp_suid() == FALSE) {
430 env_dir = getenv("MP_STATE_PATH");
431 if(env_dir && env_dir[0] != '\0')
432 return env_dir;
433 /* This is the former ENV, for backward-compatibility */
434 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
435 if(env_dir && env_dir[0] != '\0')
436 return env_dir;
439 return NP_STATE_DIR_PREFIX;
443 * Initiatializer for state routines.
444 * Sets variables. Generates filename. Returns np_state_key. die with
445 * UNKNOWN if exception
447 void np_enable_state(char *keyname, int expected_data_version) {
448 state_key *this_state = NULL;
449 char *temp_filename = NULL;
450 char *temp_keyname = NULL;
451 char *p=NULL;
452 int ret;
454 if(this_monitoring_plugin==NULL)
455 die(STATE_UNKNOWN, _("This requires np_init to be called"));
457 this_state = (state_key *) calloc(1, sizeof(state_key));
458 if(this_state==NULL)
459 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
460 strerror(errno));
462 if(keyname==NULL) {
463 temp_keyname = _np_state_generate_key();
464 } else {
465 temp_keyname = strdup(keyname);
466 if(temp_keyname==NULL)
467 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
469 /* Die if invalid characters used for keyname */
470 p = temp_keyname;
471 while(*p!='\0') {
472 if(! (isalnum(*p) || *p == '_')) {
473 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
475 p++;
477 this_state->name=temp_keyname;
478 this_state->plugin_name=this_monitoring_plugin->plugin_name;
479 this_state->data_version=expected_data_version;
480 this_state->state_data=NULL;
482 /* Calculate filename */
483 ret = asprintf(&temp_filename, "%s/%lu/%s/%s",
484 _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
485 this_monitoring_plugin->plugin_name, this_state->name);
486 if (ret < 0)
487 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
488 strerror(errno));
490 this_state->_filename=temp_filename;
492 this_monitoring_plugin->state = this_state;
496 * Will return NULL if no data is available (first run). If key currently
497 * exists, read data. If state file format version is not expected, return
498 * as if no data. Get state data version number and compares to expected.
499 * If numerically lower, then return as no previous state. die with UNKNOWN
500 * if exceptional error.
502 state_data *np_state_read() {
503 state_data *this_state_data=NULL;
504 FILE *statefile;
505 int rc = FALSE;
507 if(this_monitoring_plugin==NULL)
508 die(STATE_UNKNOWN, _("This requires np_init to be called"));
510 /* Open file. If this fails, no previous state found */
511 statefile = fopen( this_monitoring_plugin->state->_filename, "r" );
512 if(statefile!=NULL) {
514 this_state_data = (state_data *) calloc(1, sizeof(state_data));
515 if(this_state_data==NULL)
516 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
517 strerror(errno));
519 this_state_data->data=NULL;
520 this_monitoring_plugin->state->state_data = this_state_data;
522 rc = _np_state_read_file(statefile);
524 fclose(statefile);
527 if(rc==FALSE) {
528 _cleanup_state_data();
531 return this_monitoring_plugin->state->state_data;
535 * Read the state file
537 int _np_state_read_file(FILE *f) {
538 int status=FALSE;
539 size_t pos;
540 char *line;
541 int i;
542 int failure=0;
543 time_t current_time, data_time;
544 enum { STATE_FILE_VERSION, STATE_DATA_VERSION, STATE_DATA_TIME, STATE_DATA_TEXT, STATE_DATA_END } expected=STATE_FILE_VERSION;
546 time(&current_time);
548 /* Note: This introduces a limit of 1024 bytes in the string data */
549 line = (char *) calloc(1, 1024);
550 if(line==NULL)
551 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
552 strerror(errno));
554 while(!failure && (fgets(line,1024,f))!=NULL){
555 pos=strlen(line);
556 if(line[pos-1]=='\n') {
557 line[pos-1]='\0';
560 if(line[0] == '#') continue;
562 switch(expected) {
563 case STATE_FILE_VERSION:
564 i=atoi(line);
565 if(i!=NP_STATE_FORMAT_VERSION)
566 failure++;
567 else
568 expected=STATE_DATA_VERSION;
569 break;
570 case STATE_DATA_VERSION:
571 i=atoi(line);
572 if(i != this_monitoring_plugin->state->data_version)
573 failure++;
574 else
575 expected=STATE_DATA_TIME;
576 break;
577 case STATE_DATA_TIME:
578 /* If time > now, error */
579 data_time=strtoul(line,NULL,10);
580 if(data_time > current_time)
581 failure++;
582 else {
583 this_monitoring_plugin->state->state_data->time = data_time;
584 expected=STATE_DATA_TEXT;
586 break;
587 case STATE_DATA_TEXT:
588 this_monitoring_plugin->state->state_data->data = strdup(line);
589 if(this_monitoring_plugin->state->state_data->data==NULL)
590 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
591 expected=STATE_DATA_END;
592 status=TRUE;
593 break;
594 case STATE_DATA_END:
599 np_free(line);
600 return status;
604 * If time=NULL, use current time. Create state file, with state format
605 * version, default text. Writes version, time, and data. Avoid locking
606 * problems - use mv to write and then swap. Possible loss of state data if
607 * two things writing to same key at same time.
608 * Will die with UNKNOWN if errors
610 void np_state_write_string(time_t data_time, char *data_string) {
611 FILE *fp;
612 char *temp_file=NULL;
613 int fd=0, result=0;
614 time_t current_time;
615 char *directories=NULL;
616 char *p=NULL;
618 if(data_time==0)
619 time(&current_time);
620 else
621 current_time=data_time;
623 /* If file doesn't currently exist, create directories */
624 if(access(this_monitoring_plugin->state->_filename,F_OK)!=0) {
625 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
626 if(result < 0)
627 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
628 strerror(errno));
630 for(p=directories+1; *p; p++) {
631 if(*p=='/') {
632 *p='\0';
633 if((access(directories,F_OK)!=0) && (mkdir(directories, S_IRWXU)!=0)) {
634 /* Can't free this! Otherwise error message is wrong! */
635 /* np_free(directories); */
636 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
638 *p='/';
641 np_free(directories);
644 result = asprintf(&temp_file,"%s.XXXXXX",this_monitoring_plugin->state->_filename);
645 if(result < 0)
646 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
647 strerror(errno));
649 if((fd=mkstemp(temp_file))==-1) {
650 np_free(temp_file);
651 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
654 fp=(FILE *)fdopen(fd,"w");
655 if(fp==NULL) {
656 close(fd);
657 unlink(temp_file);
658 np_free(temp_file);
659 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
662 fprintf(fp,"# NP State file\n");
663 fprintf(fp,"%d\n",NP_STATE_FORMAT_VERSION);
664 fprintf(fp,"%d\n",this_monitoring_plugin->state->data_version);
665 fprintf(fp,"%lu\n",current_time);
666 fprintf(fp,"%s\n",data_string);
668 fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
670 fflush(fp);
672 result=fclose(fp);
674 fsync(fd);
676 if(result!=0) {
677 unlink(temp_file);
678 np_free(temp_file);
679 die(STATE_UNKNOWN, _("Error writing temp file"));
682 if(rename(temp_file, this_monitoring_plugin->state->_filename)!=0) {
683 unlink(temp_file);
684 np_free(temp_file);
685 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
688 np_free(temp_file);