1 /*****************************************************************************
6 * Copyright (c) 2006 Monitoring Plugins Development Team
8 * Library of useful functions for plugins
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"
29 #include "utils_base.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"),
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
;
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
;
91 die (int result
, const char *fmt
, ...)
100 if(this_monitoring_plugin
!=NULL
) {
106 void set_range_start (range
*this, double value
) {
108 this->start_infinity
= FALSE
;
111 void set_range_end (range
*this, double value
) {
113 this->end_infinity
= FALSE
;
117 *parse_range_string (char *str
) {
123 temp_range
= (range
*) calloc(1, sizeof(range
));
126 temp_range
->start
= 0;
127 temp_range
->start_infinity
= FALSE
;
129 temp_range
->end_infinity
= TRUE
;
130 temp_range
->alert_on
= OUTSIDE
;
131 temp_range
->text
= strdup(str
);
134 temp_range
->alert_on
= INSIDE
;
138 end_str
= index(str
, ':');
139 if (end_str
!= NULL
) {
141 temp_range
->start_infinity
= TRUE
;
143 start
= strtod(str
, NULL
); /* Will stop at the ':' */
144 set_range_start(temp_range
, start
);
146 end_str
++; /* Move past the ':' */
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
) {
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"),
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
;
194 set_thresholds(thresholds
**my_thresholds
, char *warn_string
, char *critical_string
)
196 switch (_set_thresholds(my_thresholds
, warn_string
, critical_string
)) {
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"));
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");
212 if (my_threshold
->warning
) {
213 printf("Warning: start=%g end=%g; ", my_threshold
->warning
->start
, my_threshold
->warning
->end
);
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
);
220 printf("Critical not set");
226 /* Returns TRUE if alert should be raised based on the range */
228 check_range(double value
, range
*my_range
)
233 if (my_range
->alert_on
== INSIDE
) {
238 if (my_range
->end_infinity
== FALSE
&& my_range
->start_infinity
== FALSE
) {
239 if ((my_range
->start
<= value
) && (value
<= my_range
->end
)) {
244 } else if (my_range
->start_infinity
== FALSE
&& my_range
->end_infinity
== TRUE
) {
245 if (my_range
->start
<= value
) {
250 } else if (my_range
->start_infinity
== TRUE
&& my_range
->end_infinity
== FALSE
) {
251 if (value
<= my_range
->end
) {
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
;
278 char *np_escaped_string (const char *string
) {
281 data
= strdup(string
);
282 for (i
=0; data
[i
]; i
++) {
283 if (data
[i
] == '\\') {
308 int np_check_if_root(void) { return (geteuid() == 0); }
311 * Extract the value from key/value pairs, or return NULL. The value returned
313 * This function can be used to parse NTP control packet data and performance
316 char *np_extract_value(const char *varlist
, const char *name
, char sep
) {
317 char *tmp
=NULL
, *value
=NULL
;
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 */
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';
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';
351 if (tmp
= index(varlist
, sep
)) {
352 /* More keys, keep going... */
360 /* Clean-up trailing spaces/newlines */
361 if (value
) for (i
=strlen(value
)-1; isspace(value
[i
]); i
--) value
[i
] = '\0';
367 state_text (int result
)
376 case STATE_DEPENDENT
:
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"))
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
;
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
;
407 char **argv
= this_monitoring_plugin
->argv
;
408 unsigned char result
[20];
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
]);
427 die(STATE_UNKNOWN
, _("Cannot execute strdup: %s"), strerror(errno
));
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(){
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')
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')
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
;
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
));
479 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
483 temp_keyname
= _np_state_generate_key();
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 */
492 if(! (isalnum(*p
) || *p
== '_')) {
493 die(STATE_UNKNOWN
, _("Invalid character for keyname - only alphanumerics or '_'"));
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
);
507 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
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
;
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"),
539 this_state_data
->data
=NULL
;
540 this_monitoring_plugin
->state
->state_data
= this_state_data
;
542 rc
= _np_state_read_file(statefile
);
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
) {
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
;
568 /* Note: This introduces a limit of 1024 bytes in the string data */
569 line
= (char *) calloc(1, 1024);
571 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
574 while(!failure
&& (fgets(line
,1024,f
))!=NULL
){
576 if(line
[pos
-1]=='\n') {
580 if(line
[0] == '#') continue;
583 case STATE_FILE_VERSION
:
585 if(i
!=NP_STATE_FORMAT_VERSION
)
588 expected
=STATE_DATA_VERSION
;
590 case STATE_DATA_VERSION
:
592 if(i
!= this_monitoring_plugin
->state
->data_version
)
595 expected
=STATE_DATA_TIME
;
597 case STATE_DATA_TIME
:
598 /* If time > now, error */
599 data_time
=strtoul(line
,NULL
,10);
600 if(data_time
> current_time
)
603 this_monitoring_plugin
->state
->state_data
->time
= data_time
;
604 expected
=STATE_DATA_TEXT
;
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
;
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
) {
632 char *temp_file
=NULL
;
635 char *directories
=NULL
;
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
);
647 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
650 for(p
=directories
+1; *p
; p
++) {
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
);
661 np_free(directories
);
664 result
= asprintf(&temp_file
,"%s.XXXXXX",this_monitoring_plugin
->state
->_filename
);
666 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
669 if((fd
=mkstemp(temp_file
))==-1) {
671 die(STATE_UNKNOWN
, _("Cannot create temporary filename"));
674 fp
=(FILE *)fdopen(fd
,"w");
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
);
699 die(STATE_UNKNOWN
, _("Error writing temp file"));
702 if(rename(temp_file
, this_monitoring_plugin
->state
->_filename
)!=0) {
705 die(STATE_UNKNOWN
, _("Cannot rename state temp file"));