1 /*****************************************************************************
6 * Copyright (c) 2006 Nagios 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"
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"),
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
;
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
;
83 die (int result
, const char *fmt
, ...)
89 if(this_nagios_plugin
!=NULL
) {
95 void set_range_start (range
*this, double value
) {
97 this->start_infinity
= FALSE
;
100 void set_range_end (range
*this, double value
) {
102 this->end_infinity
= FALSE
;
106 *parse_range_string (char *str
) {
112 temp_range
= (range
*) calloc(1, sizeof(range
));
115 temp_range
->start
= 0;
116 temp_range
->start_infinity
= FALSE
;
118 temp_range
->end_infinity
= TRUE
;
119 temp_range
->alert_on
= OUTSIDE
;
122 temp_range
->alert_on
= INSIDE
;
126 end_str
= index(str
, ':');
127 if (end_str
!= NULL
) {
129 temp_range
->start_infinity
= TRUE
;
131 start
= strtod(str
, NULL
); /* Will stop at the ':' */
132 set_range_start(temp_range
, start
);
134 end_str
++; /* Move past the ':' */
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
) {
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"),
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
;
182 set_thresholds(thresholds
**my_thresholds
, char *warn_string
, char *critical_string
)
184 switch (_set_thresholds(my_thresholds
, warn_string
, critical_string
)) {
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"));
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");
200 if (my_threshold
->warning
) {
201 printf("Warning: start=%g end=%g; ", my_threshold
->warning
->start
, my_threshold
->warning
->end
);
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
);
208 printf("Critical not set");
214 /* Returns TRUE if alert should be raised based on the range */
216 check_range(double value
, range
*my_range
)
221 if (my_range
->alert_on
== INSIDE
) {
226 if (my_range
->end_infinity
== FALSE
&& my_range
->start_infinity
== FALSE
) {
227 if ((my_range
->start
<= value
) && (value
<= my_range
->end
)) {
232 } else if (my_range
->start_infinity
== FALSE
&& my_range
->end_infinity
== TRUE
) {
233 if (my_range
->start
<= value
) {
238 } else if (my_range
->start_infinity
== TRUE
&& my_range
->end_infinity
== FALSE
) {
239 if (value
<= my_range
->end
) {
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
;
266 char *np_escaped_string (const char *string
) {
269 data
= strdup(string
);
270 for (i
=0; data
[i
]; i
++) {
271 if (data
[i
] == '\\') {
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();
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");
312 * Extract the value from key/value pairs, or return NULL. The value returned
314 * This function can be used to parse NTP control packet data and performance
317 char *np_extract_value(const char *varlist
, const char *name
, char sep
) {
318 char *tmp
=NULL
, *value
=NULL
;
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 */
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';
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';
352 if (tmp
= index(varlist
, sep
)) {
353 /* More keys, keep going... */
361 /* Clean-up trailing spaces/newlines */
362 if (value
) for (i
=strlen(value
)-1; isspace(value
[i
]); i
--) value
[i
] = '\0';
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() {
375 char **argv
= this_nagios_plugin
->argv
;
376 unsigned char result
[20];
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
]);
395 die(STATE_UNKNOWN
, _("Cannot execute strdup: %s"), strerror(errno
));
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(){
415 env_dir
= getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
416 if(env_dir
&& env_dir
[0] != '\0')
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
;
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
));
437 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
441 temp_keyname
= _np_state_generate_key();
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 */
450 if(! (isalnum(*p
) || *p
== '_')) {
451 die(STATE_UNKNOWN
, _("Invalid character for keyname - only alphanumerics or '_'"));
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
;
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"),
491 this_state_data
->data
=NULL
;
492 this_nagios_plugin
->state
->state_data
= this_state_data
;
494 rc
= _np_state_read_file(statefile
);
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
) {
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
;
520 /* Note: This introduces a limit of 1024 bytes in the string data */
521 line
= (char *) calloc(1, 1024);
523 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
526 while(!failure
&& (fgets(line
,1024,f
))!=NULL
){
528 if(line
[pos
-1]=='\n') {
532 if(line
[0] == '#') continue;
535 case STATE_FILE_VERSION
:
537 if(i
!=NP_STATE_FORMAT_VERSION
)
540 expected
=STATE_DATA_VERSION
;
542 case STATE_DATA_VERSION
:
544 if(i
!= this_nagios_plugin
->state
->data_version
)
547 expected
=STATE_DATA_TIME
;
549 case STATE_DATA_TIME
:
550 /* If time > now, error */
551 data_time
=strtoul(line
,NULL
,10);
552 if(data_time
> current_time
)
555 this_nagios_plugin
->state
->state_data
->time
= data_time
;
556 expected
=STATE_DATA_TEXT
;
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
;
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
) {
584 char *temp_file
=NULL
;
587 char *directories
=NULL
;
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"),
602 for(p
=directories
+1; *p
; p
++) {
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
);
613 np_free(directories
);
616 asprintf(&temp_file
,"%s.XXXXXX",this_nagios_plugin
->state
->_filename
);
618 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
621 if((fd
=mkstemp(temp_file
))==-1) {
623 die(STATE_UNKNOWN
, _("Cannot create temporary filename"));
626 fp
=(FILE *)fdopen(fd
,"w");
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
);
651 die(STATE_UNKNOWN
, _("Error writing temp file"));
654 if(rename(temp_file
, this_nagios_plugin
->state
->_filename
)!=0) {
657 die(STATE_UNKNOWN
, _("Cannot rename state temp file"));