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"
34 #define np_free(ptr) { if(ptr) { free(ptr); ptr = NULL; } }
36 nagios_plugin
*this_nagios_plugin
=NULL
;
38 int _np_state_read_file(FILE *);
40 void np_init( char *plugin_name
, int argc
, char **argv
) {
41 if (this_nagios_plugin
==NULL
) {
42 this_nagios_plugin
= calloc(1, sizeof(nagios_plugin
));
43 if (this_nagios_plugin
==NULL
) {
44 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
47 this_nagios_plugin
->plugin_name
= strdup(plugin_name
);
48 if (this_nagios_plugin
->plugin_name
==NULL
)
49 die(STATE_UNKNOWN
, _("Cannot execute strdup: %s"), strerror(errno
));
50 this_nagios_plugin
->argc
= argc
;
51 this_nagios_plugin
->argv
= argv
;
55 void np_set_args( int argc
, char **argv
) {
56 if (this_nagios_plugin
==NULL
)
57 die(STATE_UNKNOWN
, _("This requires np_init to be called"));
59 this_nagios_plugin
->argc
= argc
;
60 this_nagios_plugin
->argv
= argv
;
65 if (this_nagios_plugin
!=NULL
) {
66 if(this_nagios_plugin
->state
!=NULL
) {
67 if(this_nagios_plugin
->state
->state_data
) {
68 np_free(this_nagios_plugin
->state
->state_data
->data
);
69 np_free(this_nagios_plugin
->state
->state_data
);
71 np_free(this_nagios_plugin
->state
->name
);
72 np_free(this_nagios_plugin
->state
);
74 np_free(this_nagios_plugin
->plugin_name
);
75 np_free(this_nagios_plugin
);
77 this_nagios_plugin
=NULL
;
80 /* Hidden function to get a pointer to this_nagios_plugin for testing */
81 void _get_nagios_plugin( nagios_plugin
**pointer
){
82 *pointer
= this_nagios_plugin
;
86 die (int result
, const char *fmt
, ...)
92 if(this_nagios_plugin
!=NULL
) {
98 void set_range_start (range
*this, double value
) {
100 this->start_infinity
= FALSE
;
103 void set_range_end (range
*this, double value
) {
105 this->end_infinity
= FALSE
;
109 *parse_range_string (char *str
) {
115 temp_range
= (range
*) calloc(1, sizeof(range
));
118 temp_range
->start
= 0;
119 temp_range
->start_infinity
= FALSE
;
121 temp_range
->end_infinity
= TRUE
;
122 temp_range
->alert_on
= OUTSIDE
;
125 temp_range
->alert_on
= INSIDE
;
129 end_str
= index(str
, ':');
130 if (end_str
!= NULL
) {
132 temp_range
->start_infinity
= TRUE
;
134 start
= strtod(str
, NULL
); /* Will stop at the ':' */
135 set_range_start(temp_range
, start
);
137 end_str
++; /* Move past the ':' */
141 end
= strtod(end_str
, NULL
);
142 if (strcmp(end_str
, "") != 0) {
143 set_range_end(temp_range
, end
);
146 if (temp_range
->start_infinity
== TRUE
||
147 temp_range
->end_infinity
== TRUE
||
148 temp_range
->start
<= temp_range
->end
) {
155 /* returns 0 if okay, otherwise 1 */
157 _set_thresholds(thresholds
**my_thresholds
, char *warn_string
, char *critical_string
)
159 thresholds
*temp_thresholds
= NULL
;
161 if ((temp_thresholds
= calloc(1, sizeof(thresholds
))) == NULL
)
162 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
165 temp_thresholds
->warning
= NULL
;
166 temp_thresholds
->critical
= NULL
;
168 if (warn_string
!= NULL
) {
169 if ((temp_thresholds
->warning
= parse_range_string(warn_string
)) == NULL
) {
170 return NP_RANGE_UNPARSEABLE
;
173 if (critical_string
!= NULL
) {
174 if ((temp_thresholds
->critical
= parse_range_string(critical_string
)) == NULL
) {
175 return NP_RANGE_UNPARSEABLE
;
179 *my_thresholds
= temp_thresholds
;
185 set_thresholds(thresholds
**my_thresholds
, char *warn_string
, char *critical_string
)
187 switch (_set_thresholds(my_thresholds
, warn_string
, critical_string
)) {
190 case NP_RANGE_UNPARSEABLE
:
191 die(STATE_UNKNOWN
, _("Range format incorrect"));
192 case NP_WARN_WITHIN_CRIT
:
193 die(STATE_UNKNOWN
, _("Warning level is a subset of critical and will not be alerted"));
198 void print_thresholds(const char *threshold_name
, thresholds
*my_threshold
) {
199 printf("%s - ", threshold_name
);
200 if (! my_threshold
) {
201 printf("Threshold not set");
203 if (my_threshold
->warning
) {
204 printf("Warning: start=%g end=%g; ", my_threshold
->warning
->start
, my_threshold
->warning
->end
);
206 printf("Warning not set; ");
208 if (my_threshold
->critical
) {
209 printf("Critical: start=%g end=%g", my_threshold
->critical
->start
, my_threshold
->critical
->end
);
211 printf("Critical not set");
217 /* Returns TRUE if alert should be raised based on the range */
219 check_range(double value
, range
*my_range
)
224 if (my_range
->alert_on
== INSIDE
) {
229 if (my_range
->end_infinity
== FALSE
&& my_range
->start_infinity
== FALSE
) {
230 if ((my_range
->start
<= value
) && (value
<= my_range
->end
)) {
235 } else if (my_range
->start_infinity
== FALSE
&& my_range
->end_infinity
== TRUE
) {
236 if (my_range
->start
<= value
) {
241 } else if (my_range
->start_infinity
== TRUE
&& my_range
->end_infinity
== FALSE
) {
242 if (value
<= my_range
->end
) {
254 get_status(double value
, thresholds
*my_thresholds
)
256 if (my_thresholds
->critical
!= NULL
) {
257 if (check_range(value
, my_thresholds
->critical
) == TRUE
) {
258 return STATE_CRITICAL
;
261 if (my_thresholds
->warning
!= NULL
) {
262 if (check_range(value
, my_thresholds
->warning
) == TRUE
) {
263 return STATE_WARNING
;
269 char *np_escaped_string (const char *string
) {
272 data
= strdup(string
);
273 for (i
=0; data
[i
]; i
++) {
274 if (data
[i
] == '\\') {
299 int np_check_if_root(void) { return (geteuid() == 0); }
301 int np_warn_if_not_root(void) {
302 int status
= np_check_if_root();
304 printf(_("Warning: "));
305 printf(_("This plugin must be either run as root or setuid root.\n"));
306 printf(_("To run as root, you can use a tool like sudo.\n"));
307 printf(_("To set the setuid permissions, use the command:\n"));
308 /* XXX could we use something like progname? */
309 printf("\tchmod u+s yourpluginfile\n");
315 * Extract the value from key/value pairs, or return NULL. The value returned
317 * This function can be used to parse NTP control packet data and performance
320 char *np_extract_value(const char *varlist
, const char *name
, char sep
) {
321 char *tmp
=NULL
, *value
=NULL
;
325 /* Strip any leading space */
326 for (varlist
; isspace(varlist
[0]); varlist
++);
328 if (strncmp(name
, varlist
, strlen(name
)) == 0) {
329 varlist
+= strlen(name
);
330 /* strip trailing spaces */
331 for (varlist
; isspace(varlist
[0]); varlist
++);
333 if (varlist
[0] == '=') {
334 /* We matched the key, go past the = sign */
336 /* strip leading spaces */
337 for (varlist
; isspace(varlist
[0]); varlist
++);
339 if (tmp
= index(varlist
, sep
)) {
340 /* Value is delimited by a comma */
341 if (tmp
-varlist
== 0) continue;
342 value
= (char *)calloc(1, tmp
-varlist
+1);
343 strncpy(value
, varlist
, tmp
-varlist
);
344 value
[tmp
-varlist
] = '\0';
346 /* Value is delimited by a \0 */
347 if (strlen(varlist
) == 0) continue;
348 value
= (char *)calloc(1, strlen(varlist
) + 1);
349 strncpy(value
, varlist
, strlen(varlist
));
350 value
[strlen(varlist
)] = '\0';
355 if (tmp
= index(varlist
, sep
)) {
356 /* More keys, keep going... */
364 /* Clean-up trailing spaces/newlines */
365 if (value
) for (i
=strlen(value
)-1; isspace(value
[i
]); i
--) value
[i
] = '\0';
371 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
372 * hopefully a unique key per service/plugin invocation. Use the extra-opts
373 * parse of argv, so that uniqueness in parameters are reflected there.
375 char *_np_state_generate_key() {
378 char **argv
= this_nagios_plugin
->argv
;
379 unsigned char result
[20];
385 for(i
=0; i
<this_nagios_plugin
->argc
; i
++) {
386 sha1_process_bytes(argv
[i
], strlen(argv
[i
]), &ctx
);
389 sha1_finish_ctx(&ctx
, &result
);
391 for (i
=0; i
<20; ++i
) {
392 sprintf(&keyname
[2*i
], "%02x", result
[i
]);
398 die(STATE_UNKNOWN
, _("Cannot execute strdup: %s"), strerror(errno
));
403 void _cleanup_state_data() {
404 if (this_nagios_plugin
->state
->state_data
!=NULL
) {
405 np_free(this_nagios_plugin
->state
->state_data
->data
);
406 np_free(this_nagios_plugin
->state
->state_data
);
411 * Internal function. Returns either:
412 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
413 * statically compiled shared state directory
415 char* _np_state_calculate_location_prefix(){
418 env_dir
= getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
419 if(env_dir
&& env_dir
[0] != '\0')
421 return NP_STATE_DIR_PREFIX
;
425 * Initiatializer for state routines.
426 * Sets variables. Generates filename. Returns np_state_key. die with
427 * UNKNOWN if exception
429 void np_enable_state(char *keyname
, int expected_data_version
) {
430 state_key
*this_state
= NULL
;
431 char *temp_filename
= NULL
;
432 char *temp_keyname
= NULL
;
435 if(this_nagios_plugin
==NULL
)
436 die(STATE_UNKNOWN
, _("This requires np_init to be called"));
438 this_state
= (state_key
*) calloc(1, sizeof(state_key
));
440 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
444 temp_keyname
= _np_state_generate_key();
446 temp_keyname
= strdup(keyname
);
447 if(temp_keyname
==NULL
)
448 die(STATE_UNKNOWN
, _("Cannot execute strdup: %s"), strerror(errno
));
450 /* Die if invalid characters used for keyname */
453 if(! (isalnum(*p
) || *p
== '_')) {
454 die(STATE_UNKNOWN
, _("Invalid character for keyname - only alphanumerics or '_'"));
458 this_state
->name
=temp_keyname
;
459 this_state
->plugin_name
=this_nagios_plugin
->plugin_name
;
460 this_state
->data_version
=expected_data_version
;
461 this_state
->state_data
=NULL
;
463 /* Calculate filename */
464 asprintf(&temp_filename
, "%s/%s/%s", _np_state_calculate_location_prefix(), this_nagios_plugin
->plugin_name
, this_state
->name
);
465 this_state
->_filename
=temp_filename
;
467 this_nagios_plugin
->state
= this_state
;
471 * Will return NULL if no data is available (first run). If key currently
472 * exists, read data. If state file format version is not expected, return
473 * as if no data. Get state data version number and compares to expected.
474 * If numerically lower, then return as no previous state. die with UNKNOWN
475 * if exceptional error.
477 state_data
*np_state_read() {
478 state_data
*this_state_data
=NULL
;
482 if(this_nagios_plugin
==NULL
)
483 die(STATE_UNKNOWN
, _("This requires np_init to be called"));
485 /* Open file. If this fails, no previous state found */
486 statefile
= fopen( this_nagios_plugin
->state
->_filename
, "r" );
487 if(statefile
!=NULL
) {
489 this_state_data
= (state_data
*) calloc(1, sizeof(state_data
));
490 if(this_state_data
==NULL
)
491 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
494 this_state_data
->data
=NULL
;
495 this_nagios_plugin
->state
->state_data
= this_state_data
;
497 rc
= _np_state_read_file(statefile
);
503 _cleanup_state_data();
506 return this_nagios_plugin
->state
->state_data
;
510 * Read the state file
512 int _np_state_read_file(FILE *f
) {
518 time_t current_time
, data_time
;
519 enum { STATE_FILE_VERSION
, STATE_DATA_VERSION
, STATE_DATA_TIME
, STATE_DATA_TEXT
, STATE_DATA_END
} expected
=STATE_FILE_VERSION
;
523 /* Note: This introduces a limit of 1024 bytes in the string data */
524 line
= (char *) calloc(1, 1024);
526 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
529 while(!failure
&& (fgets(line
,1024,f
))!=NULL
){
531 if(line
[pos
-1]=='\n') {
535 if(line
[0] == '#') continue;
538 case STATE_FILE_VERSION
:
540 if(i
!=NP_STATE_FORMAT_VERSION
)
543 expected
=STATE_DATA_VERSION
;
545 case STATE_DATA_VERSION
:
547 if(i
!= this_nagios_plugin
->state
->data_version
)
550 expected
=STATE_DATA_TIME
;
552 case STATE_DATA_TIME
:
553 /* If time > now, error */
554 data_time
=strtoul(line
,NULL
,10);
555 if(data_time
> current_time
)
558 this_nagios_plugin
->state
->state_data
->time
= data_time
;
559 expected
=STATE_DATA_TEXT
;
562 case STATE_DATA_TEXT
:
563 this_nagios_plugin
->state
->state_data
->data
= strdup(line
);
564 if(this_nagios_plugin
->state
->state_data
->data
==NULL
)
565 die(STATE_UNKNOWN
, _("Cannot execute strdup: %s"), strerror(errno
));
566 expected
=STATE_DATA_END
;
579 * If time=NULL, use current time. Create state file, with state format
580 * version, default text. Writes version, time, and data. Avoid locking
581 * problems - use mv to write and then swap. Possible loss of state data if
582 * two things writing to same key at same time.
583 * Will die with UNKNOWN if errors
585 void np_state_write_string(time_t data_time
, char *data_string
) {
587 char *temp_file
=NULL
;
590 char *directories
=NULL
;
596 current_time
=data_time
;
598 /* If file doesn't currently exist, create directories */
599 if(access(this_nagios_plugin
->state
->_filename
,F_OK
)!=0) {
600 asprintf(&directories
, "%s", this_nagios_plugin
->state
->_filename
);
601 if(directories
==NULL
)
602 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
605 for(p
=directories
+1; *p
; p
++) {
608 if((access(directories
,F_OK
)!=0) && (mkdir(directories
, S_IRWXU
)!=0)) {
609 /* Can't free this! Otherwise error message is wrong! */
610 /* np_free(directories); */
611 die(STATE_UNKNOWN
, _("Cannot create directory: %s"), directories
);
616 np_free(directories
);
619 asprintf(&temp_file
,"%s.XXXXXX",this_nagios_plugin
->state
->_filename
);
621 die(STATE_UNKNOWN
, _("Cannot allocate memory: %s"),
624 if((fd
=mkstemp(temp_file
))==-1) {
626 die(STATE_UNKNOWN
, _("Cannot create temporary filename"));
629 fp
=(FILE *)fdopen(fd
,"w");
634 die(STATE_UNKNOWN
, _("Unable to open temporary state file"));
637 fprintf(fp
,"# NP State file\n");
638 fprintf(fp
,"%d\n",NP_STATE_FORMAT_VERSION
);
639 fprintf(fp
,"%d\n",this_nagios_plugin
->state
->data_version
);
640 fprintf(fp
,"%lu\n",current_time
);
641 fprintf(fp
,"%s\n",data_string
);
643 fchmod(fd
, S_IRUSR
| S_IWUSR
| S_IRGRP
);
654 die(STATE_UNKNOWN
, _("Error writing temp file"));
657 if(rename(temp_file
, this_nagios_plugin
->state
->_filename
)!=0) {
660 die(STATE_UNKNOWN
, _("Cannot rename state temp file"));