summaryrefslogtreecommitdiffstats
path: root/lib/utils_base.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/utils_base.c')
-rw-r--r--lib/utils_base.c643
1 files changed, 173 insertions, 470 deletions
diff --git a/lib/utils_base.c b/lib/utils_base.c
index f8592f41..28e6dc47 100644
--- a/lib/utils_base.c
+++ b/lib/utils_base.c
@@ -1,30 +1,31 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* utils_base.c 3 * utils_base.c
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2006 Monitoring Plugins Development Team 6 * Copyright (c) 2006 - 2024 Monitoring Plugins Development Team
7* 7 *
8* Library of useful functions for plugins 8 * Library of useful functions for plugins
9* 9 *
10* 10 *
11* This program is free software: you can redistribute it and/or modify 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 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 13 * the Free Software Foundation, either version 3 of the License, or
14* (at your option) any later version. 14 * (at your option) any later version.
15* 15 *
16* This program is distributed in the hope that it will be useful, 16 * This program is distributed in the hope that it will be useful,
17* but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19* GNU General Public License for more details. 19 * GNU General Public License for more details.
20* 20 *
21* You should have received a copy of the GNU General Public License 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/>. 22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23* 23 *
24* 24 *
25*****************************************************************************/ 25 *****************************************************************************/
26 26
27#include "../plugins/common.h" 27#include "../plugins/common.h"
28#include "states.h"
28#include <stdarg.h> 29#include <stdarg.h>
29#include "utils_base.h" 30#include "utils_base.h"
30#include <ctype.h> 31#include <ctype.h>
@@ -33,94 +34,87 @@
33#include <unistd.h> 34#include <unistd.h>
34#include <sys/types.h> 35#include <sys/types.h>
35 36
36#define np_free(ptr) { if(ptr) { free(ptr); ptr = NULL; } } 37#define np_free(ptr) \
38 { \
39 if (ptr) { \
40 free(ptr); \
41 ptr = NULL; \
42 } \
43 }
37 44
38monitoring_plugin *this_monitoring_plugin=NULL; 45monitoring_plugin *this_monitoring_plugin = NULL;
39 46
40int timeout_state = STATE_CRITICAL; 47mp_state_enum timeout_state = STATE_CRITICAL;
41unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; 48unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT;
42 49
43bool _np_state_read_file(FILE *); 50bool _np_state_read_file(FILE *state_file);
44 51
45void np_init( char *plugin_name, int argc, char **argv ) { 52void np_init(char *plugin_name, int argc, char **argv) {
46 if (this_monitoring_plugin==NULL) { 53 if (this_monitoring_plugin == NULL) {
47 this_monitoring_plugin = calloc(1, sizeof(monitoring_plugin)); 54 this_monitoring_plugin = calloc(1, sizeof(monitoring_plugin));
48 if (this_monitoring_plugin==NULL) { 55 if (this_monitoring_plugin == NULL) {
49 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), 56 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
50 strerror(errno));
51 } 57 }
52 this_monitoring_plugin->plugin_name = strdup(plugin_name); 58 this_monitoring_plugin->plugin_name = strdup(plugin_name);
53 if (this_monitoring_plugin->plugin_name==NULL) 59 if (this_monitoring_plugin->plugin_name == NULL) {
54 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); 60 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
61 }
55 this_monitoring_plugin->argc = argc; 62 this_monitoring_plugin->argc = argc;
56 this_monitoring_plugin->argv = argv; 63 this_monitoring_plugin->argv = argv;
57 } 64 }
58} 65}
59 66
60void np_set_args( int argc, char **argv ) { 67void np_set_args(int argc, char **argv) {
61 if (this_monitoring_plugin==NULL) 68 if (this_monitoring_plugin == NULL) {
62 die(STATE_UNKNOWN, _("This requires np_init to be called")); 69 die(STATE_UNKNOWN, _("This requires np_init to be called"));
70 }
63 71
64 this_monitoring_plugin->argc = argc; 72 this_monitoring_plugin->argc = argc;
65 this_monitoring_plugin->argv = argv; 73 this_monitoring_plugin->argv = argv;
66} 74}
67 75
68 76void np_cleanup(void) {
69void np_cleanup() { 77 if (this_monitoring_plugin != NULL) {
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);
75 }
76 np_free(this_monitoring_plugin->state->name);
77 np_free(this_monitoring_plugin->state);
78 }
79 np_free(this_monitoring_plugin->plugin_name); 78 np_free(this_monitoring_plugin->plugin_name);
80 np_free(this_monitoring_plugin); 79 np_free(this_monitoring_plugin);
81 } 80 }
82 this_monitoring_plugin=NULL; 81 this_monitoring_plugin = NULL;
83} 82}
84 83
85/* Hidden function to get a pointer to this_monitoring_plugin for testing */ 84/* Hidden function to get a pointer to this_monitoring_plugin for testing */
86void _get_monitoring_plugin( monitoring_plugin **pointer ){ 85void _get_monitoring_plugin(monitoring_plugin **pointer) { *pointer = this_monitoring_plugin; }
87 *pointer = this_monitoring_plugin;
88}
89 86
90void 87void die(int result, const char *fmt, ...) {
91die (int result, const char *fmt, ...) 88 if (fmt != NULL) {
92{
93 if(fmt!=NULL) {
94 va_list ap; 89 va_list ap;
95 va_start (ap, fmt); 90 va_start(ap, fmt);
96 vprintf (fmt, ap); 91 vprintf(fmt, ap);
97 va_end (ap); 92 va_end(ap);
98 } 93 }
99 94
100 if(this_monitoring_plugin!=NULL) { 95 if (this_monitoring_plugin != NULL) {
101 np_cleanup(); 96 np_cleanup();
102 } 97 }
103 exit (result); 98 exit(result);
104} 99}
105 100
106void set_range_start (range *this, double value) { 101void set_range_start(range *this, double value) {
107 this->start = value; 102 this->start = value;
108 this->start_infinity = false; 103 this->start_infinity = false;
109} 104}
110 105
111void set_range_end (range *this, double value) { 106void set_range_end(range *this, double value) {
112 this->end = value; 107 this->end = value;
113 this->end_infinity = false; 108 this->end_infinity = false;
114} 109}
115 110
116range 111range *parse_range_string(char *str) {
117*parse_range_string (char *str) {
118 range *temp_range; 112 range *temp_range;
119 double start; 113 double start;
120 double end; 114 double end;
121 char *end_str; 115 char *end_str;
122 116
123 temp_range = (range *) calloc(1, sizeof(range)); 117 temp_range = (range *)calloc(1, sizeof(range));
124 118
125 /* Set defaults */ 119 /* Set defaults */
126 temp_range->start = 0; 120 temp_range->start = 0;
@@ -140,10 +134,10 @@ range
140 if (str[0] == '~') { 134 if (str[0] == '~') {
141 temp_range->start_infinity = true; 135 temp_range->start_infinity = true;
142 } else { 136 } else {
143 start = strtod(str, NULL); /* Will stop at the ':' */ 137 start = strtod(str, NULL); /* Will stop at the ':' */
144 set_range_start(temp_range, start); 138 set_range_start(temp_range, start);
145 } 139 }
146 end_str++; /* Move past the ':' */ 140 end_str++; /* Move past the ':' */
147 } else { 141 } else {
148 end_str = str; 142 end_str = str;
149 } 143 }
@@ -152,8 +146,7 @@ range
152 set_range_end(temp_range, end); 146 set_range_end(temp_range, end);
153 } 147 }
154 148
155 if (temp_range->start_infinity == true || 149 if (temp_range->start_infinity || temp_range->end_infinity ||
156 temp_range->end_infinity == true ||
157 temp_range->start <= temp_range->end) { 150 temp_range->start <= temp_range->end) {
158 return temp_range; 151 return temp_range;
159 } 152 }
@@ -162,14 +155,12 @@ range
162} 155}
163 156
164/* returns 0 if okay, otherwise 1 */ 157/* returns 0 if okay, otherwise 1 */
165int 158int _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) {
166_set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
167{
168 thresholds *temp_thresholds = NULL; 159 thresholds *temp_thresholds = NULL;
169 160
170 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) 161 if ((temp_thresholds = calloc(1, sizeof(thresholds))) == NULL) {
171 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), 162 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno));
172 strerror(errno)); 163 }
173 164
174 temp_thresholds->warning = NULL; 165 temp_thresholds->warning = NULL;
175 temp_thresholds->critical = NULL; 166 temp_thresholds->critical = NULL;
@@ -190,9 +181,7 @@ _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_st
190 return 0; 181 return 0;
191} 182}
192 183
193void 184void set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string) {
194set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
195{
196 switch (_set_thresholds(my_thresholds, warn_string, critical_string)) { 185 switch (_set_thresholds(my_thresholds, warn_string, critical_string)) {
197 case 0: 186 case 0:
198 return; 187 return;
@@ -206,16 +195,18 @@ set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_str
206 195
207void print_thresholds(const char *threshold_name, thresholds *my_threshold) { 196void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
208 printf("%s - ", threshold_name); 197 printf("%s - ", threshold_name);
209 if (! my_threshold) { 198 if (!my_threshold) {
210 printf("Threshold not set"); 199 printf("Threshold not set");
211 } else { 200 } else {
212 if (my_threshold->warning) { 201 if (my_threshold->warning) {
213 printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end); 202 printf("Warning: start=%g end=%g; ", my_threshold->warning->start,
203 my_threshold->warning->end);
214 } else { 204 } else {
215 printf("Warning not set; "); 205 printf("Warning not set; ");
216 } 206 }
217 if (my_threshold->critical) { 207 if (my_threshold->critical) {
218 printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end); 208 printf("Critical: start=%g end=%g", my_threshold->critical->start,
209 my_threshold->critical->end);
219 } else { 210 } else {
220 printf("Critical not set"); 211 printf("Critical not set");
221 } 212 }
@@ -223,9 +214,38 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
223 printf("\n"); 214 printf("\n");
224} 215}
225 216
217/* Returns true if alert should be raised based on the range, false otherwise */
218bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) {
219 bool is_inside = false;
220
221 if (!my_range.end_infinity && !my_range.start_infinity) {
222 // range: .........|---inside---|...........
223 // value
224 is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) &&
225 (cmp_perfdata_value(value, my_range.end) <= 0));
226 } else if (!my_range.start_infinity && my_range.end_infinity) {
227 // range: .........|---inside---------
228 // value
229 is_inside = (cmp_perfdata_value(value, my_range.start) >= 0);
230 } else if (my_range.start_infinity && !my_range.end_infinity) {
231 // range: -inside--------|....................
232 // value
233 is_inside = (cmp_perfdata_value(value, my_range.end) == -1);
234 } else {
235 // range from -inf to inf, so always inside
236 is_inside = true;
237 }
238
239 if ((is_inside && my_range.alert_on_inside_range == INSIDE) ||
240 (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) {
241 return true;
242 }
243
244 return false;
245}
246
226/* Returns true if alert should be raised based on the range */ 247/* Returns true if alert should be raised based on the range */
227bool check_range(double value, range *my_range) 248bool check_range(double value, range *my_range) {
228{
229 bool no = false; 249 bool no = false;
230 bool yes = true; 250 bool yes = true;
231 251
@@ -234,73 +254,71 @@ bool check_range(double value, range *my_range)
234 yes = false; 254 yes = false;
235 } 255 }
236 256
237 if (my_range->end_infinity == false && my_range->start_infinity == false) { 257 if (!my_range->end_infinity && !my_range->start_infinity) {
238 if ((my_range->start <= value) && (value <= my_range->end)) { 258 if ((my_range->start <= value) && (value <= my_range->end)) {
239 return no; 259 return no;
240 } else {
241 return yes;
242 } 260 }
243 } else if (my_range->start_infinity == false && my_range->end_infinity == true) { 261 return yes;
262 }
263
264 if (!my_range->start_infinity && my_range->end_infinity) {
244 if (my_range->start <= value) { 265 if (my_range->start <= value) {
245 return no; 266 return no;
246 } else {
247 return yes;
248 } 267 }
249 } else if (my_range->start_infinity == true && my_range->end_infinity == false) { 268 return yes;
269 }
270
271 if (my_range->start_infinity && !my_range->end_infinity) {
250 if (value <= my_range->end) { 272 if (value <= my_range->end) {
251 return no; 273 return no;
252 } else {
253 return yes;
254 } 274 }
255 } else { 275 return yes;
256 return no;
257 } 276 }
277 return no;
258} 278}
259 279
260/* Returns status */ 280/* Returns status */
261int 281mp_state_enum get_status(double value, thresholds *my_thresholds) {
262get_status(double value, thresholds *my_thresholds)
263{
264 if (my_thresholds->critical != NULL) { 282 if (my_thresholds->critical != NULL) {
265 if (check_range(value, my_thresholds->critical) == true) { 283 if (check_range(value, my_thresholds->critical)) {
266 return STATE_CRITICAL; 284 return STATE_CRITICAL;
267 } 285 }
268 } 286 }
269 if (my_thresholds->warning != NULL) { 287 if (my_thresholds->warning != NULL) {
270 if (check_range(value, my_thresholds->warning) == true) { 288 if (check_range(value, my_thresholds->warning)) {
271 return STATE_WARNING; 289 return STATE_WARNING;
272 } 290 }
273 } 291 }
274 return STATE_OK; 292 return STATE_OK;
275} 293}
276 294
277char *np_escaped_string (const char *string) { 295char *np_escaped_string(const char *string) {
278 char *data; 296 char *data;
279 int i, j=0; 297 int write_index = 0;
280 data = strdup(string); 298 data = strdup(string);
281 for (i=0; data[i]; i++) { 299 for (int i = 0; data[i]; i++) {
282 if (data[i] == '\\') { 300 if (data[i] == '\\') {
283 switch(data[++i]) { 301 switch (data[++i]) {
284 case 'n': 302 case 'n':
285 data[j++] = '\n'; 303 data[write_index++] = '\n';
286 break; 304 break;
287 case 'r': 305 case 'r':
288 data[j++] = '\r'; 306 data[write_index++] = '\r';
289 break; 307 break;
290 case 't': 308 case 't':
291 data[j++] = '\t'; 309 data[write_index++] = '\t';
292 break; 310 break;
293 case '\\': 311 case '\\':
294 data[j++] = '\\'; 312 data[write_index++] = '\\';
295 break; 313 break;
296 default: 314 default:
297 data[j++] = data[i]; 315 data[write_index++] = data[i];
298 } 316 }
299 } else { 317 } else {
300 data[j++] = data[i]; 318 data[write_index++] = data[i];
301 } 319 }
302 } 320 }
303 data[j] = '\0'; 321 data[write_index] = '\0';
304 return data; 322 return data;
305} 323}
306 324
@@ -313,33 +331,43 @@ int np_check_if_root(void) { return (geteuid() == 0); }
313 * data strings. 331 * data strings.
314 */ 332 */
315char *np_extract_value(const char *varlist, const char *name, char sep) { 333char *np_extract_value(const char *varlist, const char *name, char sep) {
316 char *tmp=NULL, *value=NULL; 334 char *tmp = NULL;
317 int i; 335 char *value = NULL;
318 336
319 while (1) { 337 while (true) {
320 /* Strip any leading space */ 338 /* Strip any leading space */
321 for (; isspace(varlist[0]); varlist++); 339 for (; isspace(varlist[0]); varlist++) {
340 ;
341 }
322 342
323 if (strncmp(name, varlist, strlen(name)) == 0) { 343 if (strncmp(name, varlist, strlen(name)) == 0) {
324 varlist += strlen(name); 344 varlist += strlen(name);
325 /* strip trailing spaces */ 345 /* strip trailing spaces */
326 for (; isspace(varlist[0]); varlist++); 346 for (; isspace(varlist[0]); varlist++) {
347 ;
348 }
327 349
328 if (varlist[0] == '=') { 350 if (varlist[0] == '=') {
329 /* We matched the key, go past the = sign */ 351 /* We matched the key, go past the = sign */
330 varlist++; 352 varlist++;
331 /* strip leading spaces */ 353 /* strip leading spaces */
332 for (; isspace(varlist[0]); varlist++); 354 for (; isspace(varlist[0]); varlist++) {
355 ;
356 }
333 357
334 if ((tmp = index(varlist, sep))) { 358 if ((tmp = index(varlist, sep))) {
335 /* Value is delimited by a comma */ 359 /* Value is delimited by a comma */
336 if (tmp-varlist == 0) continue; 360 if (tmp - varlist == 0) {
337 value = (char *)calloc(1, tmp-varlist+1); 361 continue;
338 strncpy(value, varlist, tmp-varlist); 362 }
339 value[tmp-varlist] = '\0'; 363 value = (char *)calloc(1, (unsigned long)(tmp - varlist + 1));
364 strncpy(value, varlist, (unsigned long)(tmp - varlist));
365 value[tmp - varlist] = '\0';
340 } else { 366 } else {
341 /* Value is delimited by a \0 */ 367 /* Value is delimited by a \0 */
342 if (strlen(varlist) == 0) continue; 368 if (strlen(varlist) == 0) {
369 continue;
370 }
343 value = (char *)calloc(1, strlen(varlist) + 1); 371 value = (char *)calloc(1, strlen(varlist) + 1);
344 strncpy(value, varlist, strlen(varlist)); 372 strncpy(value, varlist, strlen(varlist));
345 value[strlen(varlist)] = '\0'; 373 value[strlen(varlist)] = '\0';
@@ -357,14 +385,16 @@ char *np_extract_value(const char *varlist, const char *name, char sep) {
357 } 385 }
358 386
359 /* Clean-up trailing spaces/newlines */ 387 /* Clean-up trailing spaces/newlines */
360 if (value) for (i=strlen(value)-1; isspace(value[i]); i--) value[i] = '\0'; 388 if (value) {
389 for (unsigned long i = strlen(value) - 1; isspace(value[i]); i--) {
390 value[i] = '\0';
391 }
392 }
361 393
362 return value; 394 return value;
363} 395}
364 396
365const char * 397const char *state_text(mp_state_enum result) {
366state_text (int result)
367{
368 switch (result) { 398 switch (result) {
369 case STATE_OK: 399 case STATE_OK:
370 return "OK"; 400 return "OK";
@@ -383,345 +413,18 @@ state_text (int result)
383 * Read a string representing a state (ok, warning... or numeric: 0, 1) and 413 * Read a string representing a state (ok, warning... or numeric: 0, 1) and
384 * return the corresponding STATE_ value or ERROR) 414 * return the corresponding STATE_ value or ERROR)
385 */ 415 */
386int mp_translate_state (char *state_text) { 416int mp_translate_state(char *state_text) {
387 if (!strcasecmp(state_text,"OK") || !strcmp(state_text,"0")) 417 if (!strcasecmp(state_text, "OK") || !strcmp(state_text, "0")) {
388 return STATE_OK; 418 return STATE_OK;
389 if (!strcasecmp(state_text,"WARNING") || !strcmp(state_text,"1"))
390 return STATE_WARNING;
391 if (!strcasecmp(state_text,"CRITICAL") || !strcmp(state_text,"2"))
392 return STATE_CRITICAL;
393 if (!strcasecmp(state_text,"UNKNOWN") || !strcmp(state_text,"3"))
394 return STATE_UNKNOWN;
395 return ERROR;
396}
397
398/*
399 * Returns a string to use as a keyname, based on an md5 hash of argv, thus
400 * hopefully a unique key per service/plugin invocation. Use the extra-opts
401 * parse of argv, so that uniqueness in parameters are reflected there.
402 */
403char *_np_state_generate_key() {
404 int i;
405 char **argv = this_monitoring_plugin->argv;
406 char keyname[41];
407 char *p=NULL;
408
409 unsigned char result[256];
410
411#ifdef USE_OPENSSL
412 /*
413 * This code path is chosen if openssl is available (which should be the most common
414 * scenario). Alternatively, the gnulib implementation/
415 *
416 */
417 EVP_MD_CTX *ctx = EVP_MD_CTX_new();
418
419 EVP_DigestInit(ctx, EVP_sha256());
420
421 for(i=0; i<this_monitoring_plugin->argc; i++) {
422 EVP_DigestUpdate(ctx, argv[i], strlen(argv[i]));
423 }
424
425 EVP_DigestFinal(ctx, result, NULL);
426#else
427
428 struct sha256_ctx ctx;
429
430 for(i=0; i<this_monitoring_plugin->argc; i++) {
431 sha256_process_bytes(argv[i], strlen(argv[i]), &ctx);
432 }
433
434 sha256_finish_ctx(&ctx, result);
435#endif // FOUNDOPENSSL
436
437 for (i=0; i<20; ++i) {
438 sprintf(&keyname[2*i], "%02x", result[i]);
439 } 419 }
440 420 if (!strcasecmp(state_text, "WARNING") || !strcmp(state_text, "1")) {
441 keyname[40]='\0'; 421 return STATE_WARNING;
442
443 p = strdup(keyname);
444 if(p==NULL) {
445 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
446 }
447 return p;
448}
449
450void _cleanup_state_data() {
451 if (this_monitoring_plugin->state->state_data!=NULL) {
452 np_free(this_monitoring_plugin->state->state_data->data);
453 np_free(this_monitoring_plugin->state->state_data);
454 }
455}
456
457/*
458 * Internal function. Returns either:
459 * envvar NAGIOS_PLUGIN_STATE_DIRECTORY
460 * statically compiled shared state directory
461 */
462char* _np_state_calculate_location_prefix(){
463 char *env_dir;
464
465 /* Do not allow passing MP_STATE_PATH in setuid plugins
466 * for security reasons */
467 if (!mp_suid()) {
468 env_dir = getenv("MP_STATE_PATH");
469 if(env_dir && env_dir[0] != '\0')
470 return env_dir;
471 /* This is the former ENV, for backward-compatibility */
472 env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
473 if(env_dir && env_dir[0] != '\0')
474 return env_dir;
475 }
476
477 return NP_STATE_DIR_PREFIX;
478}
479
480/*
481 * Initiatializer for state routines.
482 * Sets variables. Generates filename. Returns np_state_key. die with
483 * UNKNOWN if exception
484 */
485void np_enable_state(char *keyname, int expected_data_version) {
486 state_key *this_state = NULL;
487 char *temp_filename = NULL;
488 char *temp_keyname = NULL;
489 char *p=NULL;
490 int ret;
491
492 if(this_monitoring_plugin==NULL)
493 die(STATE_UNKNOWN, _("This requires np_init to be called"));
494
495 this_state = (state_key *) calloc(1, sizeof(state_key));
496 if(this_state==NULL)
497 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
498 strerror(errno));
499
500 if(keyname==NULL) {
501 temp_keyname = _np_state_generate_key();
502 } else {
503 temp_keyname = strdup(keyname);
504 if(temp_keyname==NULL)
505 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
506 }
507 /* Die if invalid characters used for keyname */
508 p = temp_keyname;
509 while(*p!='\0') {
510 if(! (isalnum(*p) || *p == '_')) {
511 die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
512 }
513 p++;
514 }
515 this_state->name=temp_keyname;
516 this_state->plugin_name=this_monitoring_plugin->plugin_name;
517 this_state->data_version=expected_data_version;
518 this_state->state_data=NULL;
519
520 /* Calculate filename */
521 ret = asprintf(&temp_filename, "%s/%lu/%s/%s",
522 _np_state_calculate_location_prefix(), (unsigned long)geteuid(),
523 this_monitoring_plugin->plugin_name, this_state->name);
524 if (ret < 0)
525 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
526 strerror(errno));
527
528 this_state->_filename=temp_filename;
529
530 this_monitoring_plugin->state = this_state;
531}
532
533/*
534 * Will return NULL if no data is available (first run). If key currently
535 * exists, read data. If state file format version is not expected, return
536 * as if no data. Get state data version number and compares to expected.
537 * If numerically lower, then return as no previous state. die with UNKNOWN
538 * if exceptional error.
539 */
540state_data *np_state_read() {
541 state_data *this_state_data=NULL;
542 FILE *statefile;
543 bool rc = false;
544
545 if(this_monitoring_plugin==NULL)
546 die(STATE_UNKNOWN, _("This requires np_init to be called"));
547
548 /* Open file. If this fails, no previous state found */
549 statefile = fopen( this_monitoring_plugin->state->_filename, "r" );
550 if(statefile!=NULL) {
551
552 this_state_data = (state_data *) calloc(1, sizeof(state_data));
553 if(this_state_data==NULL)
554 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
555 strerror(errno));
556
557 this_state_data->data=NULL;
558 this_monitoring_plugin->state->state_data = this_state_data;
559
560 rc = _np_state_read_file(statefile);
561
562 fclose(statefile);
563 }
564
565 if(!rc) {
566 _cleanup_state_data();
567 }
568
569 return this_monitoring_plugin->state->state_data;
570}
571
572/*
573 * Read the state file
574 */
575bool _np_state_read_file(FILE *f) {
576 bool status = false;
577 size_t pos;
578 char *line;
579 int i;
580 int failure=0;
581 time_t current_time, data_time;
582 enum { STATE_FILE_VERSION, STATE_DATA_VERSION, STATE_DATA_TIME, STATE_DATA_TEXT, STATE_DATA_END } expected=STATE_FILE_VERSION;
583
584 time(&current_time);
585
586 /* Note: This introduces a limit of 1024 bytes in the string data */
587 line = (char *) calloc(1, 1024);
588 if(line==NULL)
589 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
590 strerror(errno));
591
592 while(!failure && (fgets(line,1024,f))!=NULL){
593 pos=strlen(line);
594 if(line[pos-1]=='\n') {
595 line[pos-1]='\0';
596 }
597
598 if(line[0] == '#') continue;
599
600 switch(expected) {
601 case STATE_FILE_VERSION:
602 i=atoi(line);
603 if(i!=NP_STATE_FORMAT_VERSION)
604 failure++;
605 else
606 expected=STATE_DATA_VERSION;
607 break;
608 case STATE_DATA_VERSION:
609 i=atoi(line);
610 if(i != this_monitoring_plugin->state->data_version)
611 failure++;
612 else
613 expected=STATE_DATA_TIME;
614 break;
615 case STATE_DATA_TIME:
616 /* If time > now, error */
617 data_time=strtoul(line,NULL,10);
618 if(data_time > current_time)
619 failure++;
620 else {
621 this_monitoring_plugin->state->state_data->time = data_time;
622 expected=STATE_DATA_TEXT;
623 }
624 break;
625 case STATE_DATA_TEXT:
626 this_monitoring_plugin->state->state_data->data = strdup(line);
627 if(this_monitoring_plugin->state->state_data->data==NULL)
628 die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
629 expected=STATE_DATA_END;
630 status=true;
631 break;
632 case STATE_DATA_END:
633 ;
634 }
635 }
636
637 np_free(line);
638 return status;
639}
640
641/*
642 * If time=NULL, use current time. Create state file, with state format
643 * version, default text. Writes version, time, and data. Avoid locking
644 * problems - use mv to write and then swap. Possible loss of state data if
645 * two things writing to same key at same time.
646 * Will die with UNKNOWN if errors
647 */
648void np_state_write_string(time_t data_time, char *data_string) {
649 FILE *fp;
650 char *temp_file=NULL;
651 int fd=0, result=0;
652 time_t current_time;
653 char *directories=NULL;
654 char *p=NULL;
655
656 if(data_time==0)
657 time(&current_time);
658 else
659 current_time=data_time;
660
661 /* If file doesn't currently exist, create directories */
662 if(access(this_monitoring_plugin->state->_filename,F_OK)!=0) {
663 result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename);
664 if(result < 0)
665 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
666 strerror(errno));
667
668 for(p=directories+1; *p; p++) {
669 if(*p=='/') {
670 *p='\0';
671 if((access(directories,F_OK)!=0) && (mkdir(directories, S_IRWXU)!=0)) {
672 /* Can't free this! Otherwise error message is wrong! */
673 /* np_free(directories); */
674 die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
675 }
676 *p='/';
677 }
678 }
679 np_free(directories);
680 }
681
682 result = asprintf(&temp_file,"%s.XXXXXX",this_monitoring_plugin->state->_filename);
683 if(result < 0)
684 die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
685 strerror(errno));
686
687 if((fd=mkstemp(temp_file))==-1) {
688 np_free(temp_file);
689 die(STATE_UNKNOWN, _("Cannot create temporary filename"));
690 }
691
692 fp=(FILE *)fdopen(fd,"w");
693 if(fp==NULL) {
694 close(fd);
695 unlink(temp_file);
696 np_free(temp_file);
697 die(STATE_UNKNOWN, _("Unable to open temporary state file"));
698 } 422 }
699 423 if (!strcasecmp(state_text, "CRITICAL") || !strcmp(state_text, "2")) {
700 fprintf(fp,"# NP State file\n"); 424 return STATE_CRITICAL;
701 fprintf(fp,"%d\n",NP_STATE_FORMAT_VERSION);
702 fprintf(fp,"%d\n",this_monitoring_plugin->state->data_version);
703 fprintf(fp,"%lu\n",current_time);
704 fprintf(fp,"%s\n",data_string);
705
706 fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
707
708 fflush(fp);
709
710 result=fclose(fp);
711
712 fsync(fd);
713
714 if(result!=0) {
715 unlink(temp_file);
716 np_free(temp_file);
717 die(STATE_UNKNOWN, _("Error writing temp file"));
718 } 425 }
719 426 if (!strcasecmp(state_text, "UNKNOWN") || !strcmp(state_text, "3")) {
720 if(rename(temp_file, this_monitoring_plugin->state->_filename)!=0) { 427 return STATE_UNKNOWN;
721 unlink(temp_file);
722 np_free(temp_file);
723 die(STATE_UNKNOWN, _("Cannot rename state temp file"));
724 } 428 }
725 429 return ERROR;
726 np_free(temp_file);
727} 430}