check_tcp: Don't close connection too early
[monitoring-plugins.git] / lib / utils_base.c
blob8685e6feea22956927572ec91fedeaf5865769dd
1 /*****************************************************************************
3 * utils_base.c
5 * License: GPL
6 * Copyright (c) 2006 Nagios 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 <fcntl.h>
31 #include <sys/stat.h>
33 #define np_free(ptr) { if(ptr) { free(ptr); ptr = NULL; } }
35 nagios_plugin *this_nagios_plugin=NULL;
37 void np_init( char *plugin_name, int argc, char **argv ) {
38 if (this_nagios_plugin==NULL) {
39 this_nagios_plugin = calloc(1, sizeof(nagios_plugin));
40 if (this_nagios_plugin==NULL) {
41 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
42 strerror(errno));
44 this_nagios_plugin->plugin_name = strdup(plugin_name);
45 if (this_nagios_plugin->plugin_name==NULL)
46 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
47 this_nagios_plugin->argc = argc;
48 this_nagios_plugin->argv = argv;
52 void np_set_args( int argc, char **argv ) {
53 if (this_nagios_plugin==NULL)
54 die(STATE_UNKNOWN, _("This requires np_init to be called"));
56 this_nagios_plugin->argc = argc;
57 this_nagios_plugin->argv = argv;
61 void np_cleanup() {
62 if (this_nagios_plugin!=NULL) {
63 if(this_nagios_plugin->state!=NULL) {
64 if(this_nagios_plugin->state->state_data) {
65 np_free(this_nagios_plugin->state->state_data->data);
66 np_free(this_nagios_plugin->state->state_data);
68 np_free(this_nagios_plugin->state->name);
69 np_free(this_nagios_plugin->state);
71 np_free(this_nagios_plugin->plugin_name);
72 np_free(this_nagios_plugin);
74 this_nagios_plugin=NULL;
77 /* Hidden function to get a pointer to this_nagios_plugin for testing */
78 void _get_nagios_plugin( nagios_plugin **pointer ){
79 *pointer = this_nagios_plugin;
82 void
83 die (int result, const char *fmt, ...)
85 va_list ap;
86 va_start (ap, fmt);
87 vprintf (fmt, ap);
88 va_end (ap);
89 if(this_nagios_plugin!=NULL) {
90 np_cleanup();
92 exit (result);
95 void set_range_start (range *this, double value) {
96 this->start = value;
97 this->start_infinity = FALSE;
100 void set_range_end (range *this, double value) {
101 this->end = value;
102 this->end_infinity = FALSE;
105 range
106 *parse_range_string (char *str) {
107 range *temp_range;
108 double start;
109 double end;
110 char *end_str;
112 temp_range = (range *) calloc(1, sizeof(range));
114 /* Set defaults */
115 temp_range->start = 0;
116 temp_range->start_infinity = FALSE;
117 temp_range->end = 0;
118 temp_range->end_infinity = TRUE;
119 temp_range->alert_on = OUTSIDE;
121 if (str[0] == '@') {
122 temp_range->alert_on = INSIDE;
123 str++;
126 end_str = index(str, ':');
127 if (end_str != NULL) {
128 if (str[0] == '~') {
129 temp_range->start_infinity = TRUE;
130 } else {
131 start = strtod(str, NULL); /* Will stop at the ':' */
132 set_range_start(temp_range, start);
134 end_str++; /* Move past the ':' */
135 } else {
136 end_str = str;
138 end = strtod(end_str, NULL);
139 if (strcmp(end_str, "") != 0) {
140 set_range_end(temp_range, end);
143 if (temp_range->start_infinity == TRUE ||
144 temp_range->end_infinity == TRUE ||
145 temp_range->start <= temp_range->end) {
146 return temp_range;
148 free(temp_range);
149 return NULL;
152 /* returns 0 if okay, otherwise 1 */
154 _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
156 thresholds *temp_thresholds = NULL;
158 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL)
159 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
160 strerror(errno));
162 temp_thresholds->warning = NULL;
163 temp_thresholds->critical = NULL;
165 if (warn_string != NULL) {
166 if ((temp_thresholds->warning = parse_range_string(warn_string)) == NULL) {
167 return NP_RANGE_UNPARSEABLE;
170 if (critical_string != NULL) {
171 if ((temp_thresholds->critical = parse_range_string(critical_string)) == NULL) {
172 return NP_RANGE_UNPARSEABLE;
176 *my_thresholds = temp_thresholds;
178 return 0;
181 void
182 set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
184 switch (_set_thresholds(my_thresholds, warn_string, critical_string)) {
185 case 0:
186 return;
187 case NP_RANGE_UNPARSEABLE:
188 die(STATE_UNKNOWN, _("Range format incorrect"));
189 case NP_WARN_WITHIN_CRIT:
190 die(STATE_UNKNOWN, _("Warning level is a subset of critical and will not be alerted"));
191 break;
195 void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
196 printf("%s - ", threshold_name);
197 if (! my_threshold) {
198 printf("Threshold not set");
199 } else {
200 if (my_threshold->warning) {
201 printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end);
202 } else {
203 printf("Warning not set; ");
205 if (my_threshold->critical) {
206 printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end);
207 } else {
208 printf("Critical not set");
211 printf("\n");
214 /* Returns TRUE if alert should be raised based on the range */
216 check_range(double value, range *my_range)
218 int no = FALSE;
219 int yes = TRUE;
221 if (my_range->alert_on == INSIDE) {
222 no = TRUE;
223 yes = FALSE;
226 if (my_range->end_infinity == FALSE && my_range->start_infinity == FALSE) {
227 if ((my_range->start <= value) && (value <= my_range->end)) {
228 return no;
229 } else {
230 return yes;
232 } else if (my_range->start_infinity == FALSE && my_range->end_infinity == TRUE) {
233 if (my_range->start <= value) {
234 return no;
235 } else {
236 return yes;
238 } else if (my_range->start_infinity == TRUE && my_range->end_infinity == FALSE) {
239 if (value <= my_range->end) {
240 return no;
241 } else {
242 return yes;
244 } else {
245 return no;
249 /* Returns status */
251 get_status(double value, thresholds *my_thresholds)
253 if (my_thresholds->critical != NULL) {
254 if (check_range(value, my_thresholds->critical) == TRUE) {
255 return STATE_CRITICAL;
258 if (my_thresholds->warning != NULL) {
259 if (check_range(value, my_thresholds->warning) == TRUE) {
260 return STATE_WARNING;
263 return STATE_OK;
266 char *np_escaped_string (const char *string) {
267 char *data;
268 int i, j=0;
269 data = strdup(string);
270 for (i=0; data[i]; i++) {
271 if (data[i] == '\\') {
272 switch(data[++i]) {
273 case 'n':
274 data[j++] = '\n';
275 break;
276 case 'r':
277 data[j++] = '\r';
278 break;
279 case 't':
280 data[j++] = '\t';
281 break;
282 case '\\':
283 data[j++] = '\\';
284 break;
285 default:
286 data[j++] = data[i];
288 } else {
289 data[j++] = data[i];
292 data[j] = '\0';
293 return data;
296 int np_check_if_root(void) { return (geteuid() == 0); }
298 int np_warn_if_not_root(void) {
299 int status = np_check_if_root();
300 if(!status) {
301 printf(_("Warning: "));
302 printf(_("This plugin must be either run as root or setuid root.\n"));
303 printf(_("To run as root, you can use a tool like sudo.\n"));
304 printf(_("To set the setuid permissions, use the command:\n"));
305 /* XXX could we use something like progname? */
306 printf("\tchmod u+s yourpluginfile\n");
308 return status;
312 * Extract the value from key/value pairs, or return NULL. The value returned
313 * can be free()ed.
314 * This function can be used to parse NTP control packet data and performance
315 * data strings.
317 char *np_extract_value(const char *varlist, const char *name, char sep) {
318 char *tmp=NULL, *value=NULL;
319 int i;
321 while (1) {
322 /* Strip any leading space */
323 for (varlist; isspace(varlist[0]); varlist++);
325 if (strncmp(name, varlist, strlen(name)) == 0) {
326 varlist += strlen(name);
327 /* strip trailing spaces */
328 for (varlist; isspace(varlist[0]); varlist++);
330 if (varlist[0] == '=') {
331 /* We matched the key, go past the = sign */
332 varlist++;
333 /* strip leading spaces */
334 for (varlist; isspace(varlist[0]); varlist++);
336 if (tmp = index(varlist, sep)) {
337 /* Value is delimited by a comma */
338 if (tmp-varlist == 0) continue;
339 value = (char *)calloc(1, tmp-varlist+1);
340 strncpy(value, varlist, tmp-varlist);
341 value[tmp-varlist] = '\0';
342 } else {
343 /* Value is delimited by a \0 */
344 if (strlen(varlist) == 0) continue;
345 value = (char *)calloc(1, strlen(varlist) + 1);
346 strncpy(value, varlist, strlen(varlist));
347 value[strlen(varlist)] = '\0';
349 break;
352 if (tmp = index(varlist, sep)) {
353 /* More keys, keep going... */
354 varlist = tmp + 1;
355 } else {
356 /* We're done */
357 break;
361 /* Clean-up trailing spaces/newlines */
362 if (value) for (i=strlen(value)-1; isspace(value[i]); i--) value[i] = '\0';
364 return value;
368 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
369 * hopefully a unique key per service/plugin invocation. Use the extra-opts
370 * parse of argv, so that uniqueness in parameters are reflected there.
372 char *_np_state_generate_key() {
373 struct sha1_ctx ctx;
374 int i;
375 char **argv = this_nagios_plugin->argv;
376 unsigned char result[20];
377 char keyname[41];
378 char *p=NULL;
380 sha1_init_ctx(&ctx);
382 for(i=0; i<this_nagios_plugin->argc; i++) {
383 sha1_process_bytes(argv[i], strlen(argv[i]), &ctx);
386 sha1_finish_ctx(&ctx, &result);
388 for (i=0; i<20; ++i) {
389 sprintf(&keyname[2*i], "%02x", result[i]);
391 keyname[40]='\0';
393 p = strdup(keyname);
394 if(p==NULL) {
395 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
397 return p;
400 void _cleanup_state_data() {
401 if (this_nagios_plugin->state->state_data!=NULL) {
402 np_free(this_nagios_plugin->state->state_data->data);
403 np_free(this_nagios_plugin->state->state_data);
408 * Internal function. Returns either:
409 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
410 * statically compiled shared state directory
412 char* _np_state_calculate_location_prefix(){
413 char *env_dir;
415 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
416 if(env_dir && env_dir[0] != '\0')
417 return env_dir;
418 return NP_STATE_DIR_PREFIX;
422 * Initiatializer for state routines.
423 * Sets variables. Generates filename. Returns np_state_key. die with
424 * UNKNOWN if exception
426 void np_enable_state(char *keyname, int expected_data_version) {
427 state_key *this_state = NULL;
428 char *temp_filename = NULL;
429 char *temp_keyname = NULL;
430 char *p=NULL;
432 if(this_nagios_plugin==NULL)
433 die(STATE_UNKNOWN, _("This requires np_init to be called"));
435 this_state = (state_key *) calloc(1, sizeof(state_key));
436 if(this_state==NULL)
437 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
438 strerror(errno));
440 if(keyname==NULL) {
441 temp_keyname = _np_state_generate_key();
442 } else {
443 temp_keyname = strdup(keyname);
444 if(temp_keyname==NULL)
445 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
447 /* Die if invalid characters used for keyname */
448 p = temp_keyname;
449 while(*p!='\0') {
450 if(! (isalnum(*p) || *p == '_')) {
451 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
453 p++;
455 this_state->name=temp_keyname;
456 this_state->plugin_name=this_nagios_plugin->plugin_name;
457 this_state->data_version=expected_data_version;
458 this_state->state_data=NULL;
460 /* Calculate filename */
461 asprintf(&temp_filename, "%s/%s/%s", _np_state_calculate_location_prefix(), this_nagios_plugin->plugin_name, this_state->name);
462 this_state->_filename=temp_filename;
464 this_nagios_plugin->state = this_state;
468 * Will return NULL if no data is available (first run). If key currently
469 * exists, read data. If state file format version is not expected, return
470 * as if no data. Get state data version number and compares to expected.
471 * If numerically lower, then return as no previous state. die with UNKNOWN
472 * if exceptional error.
474 state_data *np_state_read() {
475 state_data *this_state_data=NULL;
476 FILE *statefile;
477 int rc = FALSE;
479 if(this_nagios_plugin==NULL)
480 die(STATE_UNKNOWN, _("This requires np_init to be called"));
482 /* Open file. If this fails, no previous state found */
483 statefile = fopen( this_nagios_plugin->state->_filename, "r" );
484 if(statefile!=NULL) {
486 this_state_data = (state_data *) calloc(1, sizeof(state_data));
487 if(this_state_data==NULL)
488 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
489 strerror(errno));
491 this_state_data->data=NULL;
492 this_nagios_plugin->state->state_data = this_state_data;
494 rc = _np_state_read_file(statefile);
496 fclose(statefile);
499 if(rc==FALSE) {
500 _cleanup_state_data();
503 return this_nagios_plugin->state->state_data;
507 * Read the state file
509 int _np_state_read_file(FILE *f) {
510 int status=FALSE;
511 size_t pos;
512 char *line;
513 int i;
514 int failure=0;
515 time_t current_time, data_time;
516 enum { STATE_FILE_VERSION, STATE_DATA_VERSION, STATE_DATA_TIME, STATE_DATA_TEXT, STATE_DATA_END } expected=STATE_FILE_VERSION;
518 time(&current_time);
520 /* Note: This introduces a limit of 1024 bytes in the string data */
521 line = (char *) calloc(1, 1024);
522 if(line==NULL)
523 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
524 strerror(errno));
526 while(!failure && (fgets(line,1024,f))!=NULL){
527 pos=strlen(line);
528 if(line[pos-1]=='\n') {
529 line[pos-1]='\0';
532 if(line[0] == '#') continue;
534 switch(expected) {
535 case STATE_FILE_VERSION:
536 i=atoi(line);
537 if(i!=NP_STATE_FORMAT_VERSION)
538 failure++;
539 else
540 expected=STATE_DATA_VERSION;
541 break;
542 case STATE_DATA_VERSION:
543 i=atoi(line);
544 if(i != this_nagios_plugin->state->data_version)
545 failure++;
546 else
547 expected=STATE_DATA_TIME;
548 break;
549 case STATE_DATA_TIME:
550 /* If time > now, error */
551 data_time=strtoul(line,NULL,10);
552 if(data_time > current_time)
553 failure++;
554 else {
555 this_nagios_plugin->state->state_data->time = data_time;
556 expected=STATE_DATA_TEXT;
558 break;
559 case STATE_DATA_TEXT:
560 this_nagios_plugin->state->state_data->data = strdup(line);
561 if(this_nagios_plugin->state->state_data->data==NULL)
562 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
563 expected=STATE_DATA_END;
564 status=TRUE;
565 break;
566 case STATE_DATA_END:
571 np_free(line);
572 return status;
576 * If time=NULL, use current time. Create state file, with state format
577 * version, default text. Writes version, time, and data. Avoid locking
578 * problems - use mv to write and then swap. Possible loss of state data if
579 * two things writing to same key at same time.
580 * Will die with UNKNOWN if errors
582 void np_state_write_string(time_t data_time, char *data_string) {
583 FILE *fp;
584 char *temp_file=NULL;
585 int fd=0, result=0;
586 time_t current_time;
587 char *directories=NULL;
588 char *p=NULL;
590 if(data_time==0)
591 time(&current_time);
592 else
593 current_time=data_time;
595 /* If file doesn't currently exist, create directories */
596 if(access(this_nagios_plugin->state->_filename,F_OK)!=0) {
597 asprintf(&directories, "%s", this_nagios_plugin->state->_filename);
598 if(directories==NULL)
599 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
600 strerror(errno));
602 for(p=directories+1; *p; p++) {
603 if(*p=='/') {
604 *p='\0';
605 if((access(directories,F_OK)!=0) && (mkdir(directories, S_IRWXU)!=0)) {
606 /* Can't free this! Otherwise error message is wrong! */
607 /* np_free(directories); */
608 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
610 *p='/';
613 np_free(directories);
616 asprintf(&temp_file,"%s.XXXXXX",this_nagios_plugin->state->_filename);
617 if(temp_file==NULL)
618 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
619 strerror(errno));
621 if((fd=mkstemp(temp_file))==-1) {
622 np_free(temp_file);
623 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
626 fp=(FILE *)fdopen(fd,"w");
627 if(fp==NULL) {
628 close(fd);
629 unlink(temp_file);
630 np_free(temp_file);
631 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
634 fprintf(fp,"# NP State file\n");
635 fprintf(fp,"%d\n",NP_STATE_FORMAT_VERSION);
636 fprintf(fp,"%d\n",this_nagios_plugin->state->data_version);
637 fprintf(fp,"%lu\n",current_time);
638 fprintf(fp,"%s\n",data_string);
640 fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
642 fflush(fp);
644 result=fclose(fp);
646 fsync(fd);
648 if(result!=0) {
649 unlink(temp_file);
650 np_free(temp_file);
651 die(STATE_UNKNOWN, _("Error writing temp file"));
654 if(rename(temp_file, this_nagios_plugin->state->_filename)!=0) {
655 unlink(temp_file);
656 np_free(temp_file);
657 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
660 np_free(temp_file);