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 *****************************************************************************/
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 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"),
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
;
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
;
88 die (int result
, const char *fmt
, ...)
97 if(this_monitoring_plugin
!=NULL
) {
103 void set_range_start (range
*this, double value
) {
105 this->start_infinity
= FALSE
;
108 void set_range_end (range
*this, double value
) {
110 this->end_infinity
= FALSE
;
114 *parse_range_string (char *str
) {
120 temp_range
= (range
*) calloc(1, sizeof(range
));
123 temp_range
->start
= 0;
124 temp_range
->start_infinity
= FALSE
;
126 temp_range
->end_infinity
= TRUE
;
127 temp_range
->alert_on
= OUTSIDE
;
130 temp_range
->alert_on
= INSIDE
;
134 end_str
= index(str
, ':');
135 if (end_str
!= NULL
) {
137 temp_range
->start_infinity
= TRUE
;
139 start
= strtod(str
, NULL
); /* Will stop at the ':' */
140 set_range_start(temp_range
, start
);
142 end_str
++; /* Move past the ':' */
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
) {
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"),
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
;
190 set_thresholds(thresholds
**my_thresholds
, char *warn_string
, char *critical_string
)
192 switch (_set_thresholds(my_thresholds
, warn_string
, critical_string
)) {
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"));
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");
208 if (my_threshold
->warning
) {
209 printf("Warning: start=%g end=%g; ", my_threshold
->warning
->start
, my_threshold
->warning
->end
);
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
);
216 printf("Critical not set");
222 /* Returns TRUE if alert should be raised based on the range */
224 check_range(double value
, range
*my_range
)
229 if (my_range
->alert_on
== INSIDE
) {
234 if (my_range
->end_infinity
== FALSE
&& my_range
->start_infinity
== FALSE
) {
235 if ((my_range
->start
<= value
) && (value
<= my_range
->end
)) {
240 } else if (my_range
->start_infinity
== FALSE
&& my_range
->end_infinity
== TRUE
) {
241 if (my_range
->start
<= value
) {
246 } else if (my_range
->start_infinity
== TRUE
&& my_range
->end_infinity
== FALSE
) {
247 if (value
<= my_range
->end
) {
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
;
274 char *np_escaped_string (const char *string
) {
277 data
= strdup(string
);
278 for (i
=0; data
[i
]; i
++) {
279 if (data
[i
] == '\\') {
304 int np_check_if_root(void) { return (geteuid() == 0); }
307 * Extract the value from key/value pairs, or return NULL. The value returned
309 * This function can be used to parse NTP control packet data and performance
312 char *np_extract_value(const char *varlist
, const char *name
, char sep
) {
313 char *tmp
=NULL
, *value
=NULL
;
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 */
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';
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';
347 if (tmp
= index(varlist
, sep
)) {
348 /* More keys, keep going... */
356 /* Clean-up trailing spaces/newlines */
357 if (value
) for (i
=strlen(value
)-1; isspace(value
[i
]); i
--) value
[i
] = '\0';
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"))
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
;
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() {
387 char **argv
= this_monitoring_plugin
->argv
;
388 unsigned char result
[20];
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
]);
407 die(STATE_UNKNOWN
, _("Cannot execute strdup: %s"), strerror(errno
));
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(){
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')
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')
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
;
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
));
459 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
463 temp_keyname
= _np_state_generate_key();
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 */
472 if(! (isalnum(*p
) || *p
== '_')) {
473 die(STATE_UNKNOWN
, _("Invalid character for keyname - only alphanumerics or '_'"));
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
);
487 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
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
;
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"),
519 this_state_data
->data
=NULL
;
520 this_monitoring_plugin
->state
->state_data
= this_state_data
;
522 rc
= _np_state_read_file(statefile
);
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
) {
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
;
548 /* Note: This introduces a limit of 1024 bytes in the string data */
549 line
= (char *) calloc(1, 1024);
551 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
554 while(!failure
&& (fgets(line
,1024,f
))!=NULL
){
556 if(line
[pos
-1]=='\n') {
560 if(line
[0] == '#') continue;
563 case STATE_FILE_VERSION
:
565 if(i
!=NP_STATE_FORMAT_VERSION
)
568 expected
=STATE_DATA_VERSION
;
570 case STATE_DATA_VERSION
:
572 if(i
!= this_monitoring_plugin
->state
->data_version
)
575 expected
=STATE_DATA_TIME
;
577 case STATE_DATA_TIME
:
578 /* If time > now, error */
579 data_time
=strtoul(line
,NULL
,10);
580 if(data_time
> current_time
)
583 this_monitoring_plugin
->state
->state_data
->time
= data_time
;
584 expected
=STATE_DATA_TEXT
;
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
;
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
) {
612 char *temp_file
=NULL
;
615 char *directories
=NULL
;
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
);
627 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
630 for(p
=directories
+1; *p
; p
++) {
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
);
641 np_free(directories
);
644 result
= asprintf(&temp_file
,"%s.XXXXXX",this_monitoring_plugin
->state
->_filename
);
646 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
649 if((fd
=mkstemp(temp_file
))==-1) {
651 die(STATE_UNKNOWN
, _("Cannot create temporary filename"));
654 fp
=(FILE *)fdopen(fd
,"w");
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
);
679 die(STATE_UNKNOWN
, _("Error writing temp file"));
682 if(rename(temp_file
, this_monitoring_plugin
->state
->_filename
)!=0) {
685 die(STATE_UNKNOWN
, _("Cannot rename state temp file"));