1 /*****************************************************************************
2 *
3 * utils_base.c
4 *
5 * License: GPL
6 * Copyright (c) 2006-2014 Nagios Plugins Development Team
7 *
8 * Library of useful functions for plugins
9 *
10 *
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.
15 *
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.
20 *
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/>.
23 *
24 *
25 *****************************************************************************/
26 
27 #include "common.h"
28 #include <stdarg.h>
29 #include "utils_base.h"
30 #include <ctype.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 
36 #define np_free(ptr) { if(ptr) { free(ptr); ptr = NULL; } }
37 
38 nagios_plugin *this_nagios_plugin=NULL;
39 
40 int _np_state_read_file(FILE *);
41 
np_init(char * plugin_name,int argc,char ** argv)42 void np_init( char *plugin_name, int argc, char **argv ) {
43 	if (!this_nagios_plugin) {
44 		this_nagios_plugin = calloc(1, sizeof(nagios_plugin));
45 		if (!this_nagios_plugin) {
46 			die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
47 		}
48 		this_nagios_plugin->plugin_name = strdup(plugin_name);
49 		if (!this_nagios_plugin->plugin_name)
50 			die(STATE_UNKNOWN, "%s %s\n", _("Cannot execute strdup:"), strerror(errno));
51 		this_nagios_plugin->argc = argc;
52 		this_nagios_plugin->argv = argv;
53 	}
54 }
55 
np_set_args(int argc,char ** argv)56 void np_set_args( int argc, char **argv ) {
57 	if (!this_nagios_plugin)
58 		die(STATE_UNKNOWN, "%s\n", _("This requires np_init to be called"));
59 
60 	this_nagios_plugin->argc = argc;
61 	this_nagios_plugin->argv = argv;
62 }
63 
64 
np_cleanup()65 void np_cleanup() {
66 	if (this_nagios_plugin) {
67 		if(this_nagios_plugin->state) {
68 			if(this_nagios_plugin->state->state_data) {
69 				np_free(this_nagios_plugin->state->state_data->data);
70 				np_free(this_nagios_plugin->state->state_data);
71 			}
72 			np_free(this_nagios_plugin->state->name);
73 			np_free(this_nagios_plugin->state);
74 		}
75 		np_free(this_nagios_plugin->plugin_name);
76 		np_free(this_nagios_plugin);
77 	}
78 	this_nagios_plugin=NULL;
79 }
80 
81 /* Hidden function to get a pointer to this_nagios_plugin for testing */
_get_nagios_plugin(nagios_plugin ** pointer)82 void _get_nagios_plugin( nagios_plugin **pointer ){
83 	*pointer = this_nagios_plugin;
84 }
85 
86 void
die(int result,const char * fmt,...)87 die (int result, const char *fmt, ...)
88 {
89 	va_list ap;
90 	va_start (ap, fmt);
91 	vprintf (fmt, ap);
92 	va_end (ap);
93 	if(this_nagios_plugin) {
94 		np_cleanup();
95 	}
96 	exit (result);
97 }
98 
set_range_start(range * this,double value)99 void set_range_start (range *this, double value) {
100 	this->start = value;
101 	this->start_infinity = FALSE;
102 }
103 
set_range_end(range * this,double value)104 void set_range_end (range *this, double value) {
105 	this->end = value;
106 	this->end_infinity = FALSE;
107 }
108 
109 range
parse_range_string(char * str)110 *parse_range_string (char *str) {
111 	range *temp_range;
112 	double start;
113 	double end;
114 	char *end_str;
115 
116 	temp_range = (range *) calloc(1, sizeof(range));
117 
118 	/* Set defaults */
119 	temp_range->start = 0;
120 	temp_range->start_infinity = FALSE;
121 	temp_range->end = 0;
122 	temp_range->end_infinity = TRUE;
123 	temp_range->alert_on = OUTSIDE;
124 
125 	if (str[0] == '@') {
126 		temp_range->alert_on = INSIDE;
127 		str++;
128 	}
129 
130 	end_str = index(str, ':');
131 	if (end_str) {
132 		if (str[0] == '~') {
133 			temp_range->start_infinity = TRUE;
134 		} else {
135 			start = strtod(str, NULL);	/* Will stop at the ':' */
136 			set_range_start(temp_range, start);
137 		}
138 		end_str++;		/* Move past the ':' */
139 	} else {
140 		end_str = str;
141 	}
142 	end = strtod(end_str, NULL);
143 	if (strcmp(end_str, "")) {
144 		set_range_end(temp_range, end);
145 	}
146 
147 	if (temp_range->start_infinity ||
148 		temp_range->end_infinity ||
149 		temp_range->start <= temp_range->end) {
150 		return temp_range;
151 	}
152 	free(temp_range);
153 	return NULL;
154 }
155 
156 /* returns 0 if okay, otherwise 1 */
157 int
_set_thresholds(thresholds ** my_thresholds,char * warn_string,char * critical_string)158 _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
159 {
160 	thresholds *temp_thresholds = NULL;
161 
162 	if (!(temp_thresholds = calloc(1, sizeof(thresholds))))
163 		die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
164 
165 	temp_thresholds->warning = NULL;
166 	temp_thresholds->critical = NULL;
167 	temp_thresholds->warning_string = NULL;
168 	temp_thresholds->critical_string = NULL;
169 
170 	if (warn_string) {
171 		if (!(temp_thresholds->warning = parse_range_string(warn_string))) {
172 			free(temp_thresholds);
173 			return NP_RANGE_UNPARSEABLE;
174 		}
175 		temp_thresholds->warning_string = strdup(warn_string);
176 	}
177 	if (critical_string) {
178 		if (!(temp_thresholds->critical = parse_range_string(critical_string))) {
179 			free(temp_thresholds);
180 			return NP_RANGE_UNPARSEABLE;
181 		}
182 		temp_thresholds->critical_string = strdup(critical_string);
183 	}
184 
185 	*my_thresholds = temp_thresholds;
186 
187 	return 0;
188 }
189 
190 void
set_thresholds(thresholds ** my_thresholds,char * warn_string,char * critical_string)191 set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
192 {
193 	switch (_set_thresholds(my_thresholds, warn_string, critical_string)) {
194 	case 0:
195 		return;
196 	case NP_RANGE_UNPARSEABLE:
197 		die(STATE_UNKNOWN, "%s\n", _("Range format incorrect"));
198 	case NP_WARN_WITHIN_CRIT:
199 		die(STATE_UNKNOWN, "%s\n", _("Warning level is a subset of critical and will not be alerted"));
200 		break;
201 	}
202 }
203 
print_thresholds(const char * threshold_name,thresholds * my_threshold)204 void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
205 	printf("%s - ", threshold_name);
206 	if (! my_threshold) {
207 		printf("Threshold not set");
208 	} else {
209 		if (my_threshold->warning) {
210 			printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end);
211 			if (my_threshold->warning_string) {
212 				printf("Warning String: %s; ", my_threshold->warning_string);
213 			}
214 		} else {
215 			printf("Warning not set; ");
216 		}
217 		if (my_threshold->critical) {
218 			printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end);
219 			if (my_threshold->critical_string) {
220 				printf("Critical String: %s; ", my_threshold->critical_string);
221 			}
222 		} else {
223 			printf("Critical not set");
224 		}
225 	}
226 	printf("\n");
227 }
228 
229 /* Returns TRUE if alert should be raised based on the range */
230 int
check_range(double value,range * my_range)231 check_range(double value, range *my_range)
232 {
233 	int no = FALSE;
234 	int yes = TRUE;
235 
236 	if (my_range->alert_on == INSIDE) {
237 		no = TRUE;
238 		yes = FALSE;
239 	}
240 
241 	if (!my_range->end_infinity && !my_range->start_infinity) {
242 		if ((my_range->start <= value) && (value <= my_range->end)) {
243 			return no;
244 		} else {
245 			return yes;
246 		}
247 	} else if (!my_range->start_infinity && my_range->end_infinity) {
248 		if (my_range->start <= value) {
249 			return no;
250 		} else {
251 			return yes;
252 		}
253 	} else if (my_range->start_infinity && !my_range->end_infinity) {
254 		if (value <= my_range->end) {
255 			return no;
256 		} else {
257 			return yes;
258 		}
259 	} else {
260 		return no;
261 	}
262 }
263 
264 /* Returns status */
265 int
get_status(double value,thresholds * my_thresholds)266 get_status(double value, thresholds *my_thresholds)
267 {
268 	if (my_thresholds->critical) {
269 		if (check_range(value, my_thresholds->critical)) {
270 			return STATE_CRITICAL;
271 		}
272 	}
273 	if (my_thresholds->warning) {
274 		if (check_range(value, my_thresholds->warning)) {
275 			return STATE_WARNING;
276 		}
277 	}
278 	return STATE_OK;
279 }
280 
np_escaped_string(const char * string)281 char *np_escaped_string (const char *string) {
282 	char *data;
283 	int i, j=0;
284 	data = strdup(string);
285 	for (i=0; data[i]; i++) {
286 		if (data[i] == '\\') {
287 			switch(data[++i]) {
288 				case 'n':
289 					data[j++] = '\n';
290 					break;
291 				case 'r':
292 					data[j++] = '\r';
293 					break;
294 				case 't':
295 					data[j++] = '\t';
296 					break;
297 				case '\\':
298 					data[j++] = '\\';
299 					break;
300 				default:
301 					data[j++] = data[i];
302 			}
303 		} else {
304 			data[j++] = data[i];
305 		}
306 	}
307 	data[j] = '\0';
308 	return data;
309 }
310 
np_check_if_root(void)311 int np_check_if_root(void) { return (geteuid() == 0); }
312 
np_warn_if_not_root(void)313 int np_warn_if_not_root(void) {
314 	int status = np_check_if_root();
315 	if(!status) {
316 		printf("%s", _("Warning: "));
317 		printf("%s\n", _("This plugin must be either run as root or setuid root."));
318 		printf("%s\n", _("To run as root, you can use a tool like sudo."));
319 		printf("%s\n", _("To set the setuid permissions, use the command:"));
320 		/* XXX could we use something like progname? */
321 		printf("\t%s\n", _("chmod u+s yourpluginfile"));
322 	}
323 	return status;
324 }
325 
326 /*
327  * Extract the value from key/value pairs, or return NULL. The value returned
328  * can be free()ed.
329  * This function can be used to parse NTP control packet data and performance
330  * data strings.
331  */
np_extract_value(const char * varlist,const char * name,char sep)332 char *np_extract_value(const char *varlist, const char *name, char sep) {
333 	char *tmp=NULL, *value=NULL;
334 	int i;
335 
336 	while (1) {
337 		/* Strip any leading space */
338 		for (; isspace(varlist[0]); varlist++);
339 
340 		if (!strncmp(name, varlist, strlen(name))) {
341 			varlist += strlen(name);
342 			/* strip trailing spaces */
343 			for (; isspace(varlist[0]); varlist++);
344 
345 			if (varlist[0] == '=') {
346 				/* We matched the key, go past the = sign */
347 				varlist++;
348 				/* strip leading spaces */
349 				for (; isspace(varlist[0]); varlist++);
350 
351 				if ((tmp = index(varlist, sep))) {
352 					/* Value is delimited by a comma */
353 					if (tmp-varlist == 0) continue;
354 					value = (char *)calloc(1, tmp-varlist+1);
355 					strncpy(value, varlist, tmp-varlist);
356 					value[tmp-varlist] = '\0';
357 				} else {
358 					/* Value is delimited by a \0 */
359 					if (!strlen(varlist)) continue;
360 					value = (char *)calloc(1, strlen(varlist) + 1);
361 					strncpy(value, varlist, strlen(varlist));
362 					value[strlen(varlist)] = '\0';
363 				}
364 				break;
365 			}
366 		}
367 		if ((tmp = index(varlist, sep))) {
368 			/* More keys, keep going... */
369 			varlist = tmp + 1;
370 		} else {
371 			/* We're done */
372 			break;
373 		}
374 	}
375 
376 	/* Clean-up trailing spaces/newlines */
377 	if (value) for (i=strlen(value)-1; isspace(value[i]); i--) value[i] = '\0';
378 
379 	return value;
380 }
381 
382 /*
383  * Read a string representing a state (ok, warning... or numeric: 0, 1) and
384  * return the corresponding STATE_ value or ERROR)
385  */
translate_state(char * state_text)386 int translate_state (char *state_text) {
387        if (!strcasecmp(state_text,"OK") || !strcmp(state_text,"0"))
388                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  */
_np_state_generate_key()403 char *_np_state_generate_key() {
404 	struct sha1_ctx ctx;
405 	int i;
406 	char **argv = this_nagios_plugin->argv;
407 	unsigned char result[20];
408 	char keyname[41];
409 	char *p=NULL;
410 
411 	sha1_init_ctx(&ctx);
412 
413 	for(i=0; i<this_nagios_plugin->argc; i++) {
414 		sha1_process_bytes(argv[i], strlen(argv[i]), &ctx);
415 	}
416 
417 	sha1_finish_ctx(&ctx, &result);
418 
419 	for (i=0; i<20; ++i) {
420 		sprintf(&keyname[2*i], "%02x", result[i]);
421 	}
422 	keyname[40]='\0';
423 
424 	p = strdup(keyname);
425 	if(!p) {
426 		die(STATE_UNKNOWN, "%s %s\n", _("Cannot execute strdup:"), strerror(errno));
427 	}
428 	return p;
429 }
430 
_cleanup_state_data()431 void _cleanup_state_data() {
432 	if (this_nagios_plugin->state->state_data) {
433 		np_free(this_nagios_plugin->state->state_data->data);
434 		np_free(this_nagios_plugin->state->state_data);
435 	}
436 }
437 
438 /*
439  * Internal function. Returns either:
440  *   envvar NAGIOS_PLUGIN_STATE_DIRECTORY
441  *   statically compiled shared state directory
442  */
_np_state_calculate_location_prefix()443 char* _np_state_calculate_location_prefix(){
444 	char *env_dir;
445 
446 	/* Do not allow passing NP_STATE_DIRECTORY in setuid plugins
447 	 * for security reasons */
448 
449 	if (!np_suid()) {
450 		env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
451 		if(env_dir && env_dir[0] != '\0')
452 			return env_dir;
453 		return NP_STATE_DIR_PREFIX;
454 	}
455 }
456 
457 /*
458  * Initiatializer for state routines.
459  * Sets variables. Generates filename. Returns np_state_key. die with
460  * UNKNOWN if exception
461  */
np_enable_state(char * keyname,int expected_data_version)462 void np_enable_state(char *keyname, int expected_data_version) {
463 	state_key *this_state = NULL;
464 	char *temp_filename = NULL;
465 	char *temp_keyname = NULL;
466 	char *p=NULL;
467 	int ret;
468 
469 	if(!this_nagios_plugin)
470 		die(STATE_UNKNOWN, "%s\n", _("This requires np_init to be called"));
471 
472 	this_state = (state_key *) calloc(1, sizeof(state_key));
473 	if(!this_state)
474 		die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
475 
476 	if(!keyname) {
477 		temp_keyname = _np_state_generate_key();
478 	} else {
479 		temp_keyname = strdup(keyname);
480 		if(!temp_keyname)
481 			die(STATE_UNKNOWN, "%s %s\n", _("Cannot execute strdup:"), strerror(errno));
482 	}
483 	/* Die if invalid characters used for keyname */
484 	p = temp_keyname;
485 	while(*p!='\0') {
486 		if(! (isalnum(*p) || *p == '_'))
487 			die(STATE_UNKNOWN, "%s\n", _("Invalid character for keyname - only alphanumerics or '_'"));
488 		p++;
489 	}
490 	this_state->name=temp_keyname;
491 	this_state->plugin_name=this_nagios_plugin->plugin_name;
492 	this_state->data_version=expected_data_version;
493 	this_state->state_data=NULL;
494 
495 	/* Calculate filename */
496 	ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(), this_nagios_plugin->plugin_name, this_state->name);
497 	if (ret < 0)
498 		die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
499 	this_state->_filename=temp_filename;
500 
501 	this_nagios_plugin->state = this_state;
502 }
503 
504 /*
505  * Will return NULL if no data is available (first run). If key currently
506  * exists, read data. If state file format version is not expected, return
507  * as if no data. Get state data version number and compares to expected.
508  * If numerically lower, then return as no previous state. die with UNKNOWN
509  * if exceptional error.
510  */
np_state_read()511 state_data *np_state_read() {
512 	state_data *this_state_data=NULL;
513 	FILE *statefile;
514 	int rc = FALSE;
515 
516 	if(!this_nagios_plugin)
517 		die(STATE_UNKNOWN, "%s\n", _("This requires np_init to be called"));
518 
519 	/* Open file. If this fails, no previous state found */
520 	statefile = fopen( this_nagios_plugin->state->_filename, "r" );
521 	if(statefile) {
522 
523 		this_state_data = (state_data *) calloc(1, sizeof(state_data));
524 		if(!this_state_data)
525 			die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
526 
527 		this_state_data->data=NULL;
528 		this_nagios_plugin->state->state_data = this_state_data;
529 
530 		rc = _np_state_read_file(statefile);
531 
532 		fclose(statefile);
533 	}
534 
535 	if(!rc) {
536 		_cleanup_state_data();
537 	}
538 
539 	return this_nagios_plugin->state->state_data;
540 }
541 
542 /*
543  * Read the state file
544  */
_np_state_read_file(FILE * f)545 int _np_state_read_file(FILE *f) {
546 	int status=FALSE;
547 	size_t pos;
548 	char *line;
549 	int i;
550 	int failure=0;
551 	time_t current_time, data_time;
552 	enum { STATE_FILE_VERSION, STATE_DATA_VERSION, STATE_DATA_TIME, STATE_DATA_TEXT, STATE_DATA_END } expected=STATE_FILE_VERSION;
553 
554 	time(&current_time);
555 
556 	/* Note: This introduces a limit of 1024 bytes in the string data */
557 	line = (char *) calloc(1, 1024);
558 	if(!line)
559 		die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
560 
561 	while(!failure && (fgets(line,1024,f))!=NULL){
562 		pos=strlen(line);
563 		if(line[pos-1]=='\n')
564 			line[pos-1]='\0';
565 
566 		if(line[0] == '#') continue;
567 
568 		switch(expected) {
569 			case STATE_FILE_VERSION:
570 				i=atoi(line);
571 				if(i!=NP_STATE_FORMAT_VERSION)
572 					failure++;
573 				else
574 					expected=STATE_DATA_VERSION;
575 				break;
576 			case STATE_DATA_VERSION:
577 				i=atoi(line);
578 				if(i != this_nagios_plugin->state->data_version)
579 					failure++;
580 				else
581 					expected=STATE_DATA_TIME;
582 				break;
583 			case STATE_DATA_TIME:
584 				/* If time > now, error */
585 				data_time=strtoul(line,NULL,10);
586 				if(data_time > current_time)
587 					failure++;
588 				else {
589 					this_nagios_plugin->state->state_data->time = data_time;
590 					expected=STATE_DATA_TEXT;
591 				}
592 				break;
593 			case STATE_DATA_TEXT:
594 				this_nagios_plugin->state->state_data->data = strdup(line);
595 				if(this_nagios_plugin->state->state_data->data==NULL)
596 					die(STATE_UNKNOWN, "%s %s\n", _("Cannot execute strdup:"), strerror(errno));
597 				expected=STATE_DATA_END;
598 				status=TRUE;
599 				break;
600 			case STATE_DATA_END:
601 				;
602 		}
603 	}
604 
605 	np_free(line);
606 	return status;
607 }
608 
609 /*
610  * If time=NULL, use current time. Create state file, with state format
611  * version, default text. Writes version, time, and data. Avoid locking
612  * problems - use mv to write and then swap. Possible loss of state data if
613  * two things writing to same key at same time.
614  * Will die with UNKNOWN if errors
615  */
np_state_write_string(time_t data_time,char * data_string)616 void np_state_write_string(time_t data_time, char *data_string) {
617 	FILE *fp;
618 	char *temp_file=NULL;
619 	int fd=0, result=0;
620 	time_t current_time;
621 	char *directories=NULL;
622 	char *p=NULL;
623 
624 	if(!data_time)
625 		time(&current_time);
626 	else
627 		current_time=data_time;
628 
629 	/* If file doesn't currently exist, create directories */
630 	if(access(this_nagios_plugin->state->_filename,F_OK)) {
631 		result = asprintf(&directories, "%s", this_nagios_plugin->state->_filename);
632 		if (result < 0)
633 			die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
634 		if(!directories)
635 			die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
636 
637 		for(p=directories+1; *p; p++) {
638 			if(*p=='/') {
639 				*p='\0';
640 				if(access(directories,F_OK) && mkdir(directories, S_IRWXU)) {
641 					/* Can't free this! Otherwise error message is wrong! */
642 					/* np_free(directories); */
643 					die(STATE_UNKNOWN, "%s %s\n", _("Cannot create directory:"), directories);
644 				}
645 				*p='/';
646 			}
647 		}
648 		np_free(directories);
649 	}
650 
651 	result = asprintf(&temp_file,"%s.XXXXXX",this_nagios_plugin->state->_filename);
652 	if (result < 0)
653 		die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
654 	if(!temp_file)
655 		die(STATE_UNKNOWN, "%s %s\n", _("Cannot allocate memory:"), strerror(errno));
656 
657 	if((fd=mkstemp(temp_file))==-1) {
658 		np_free(temp_file);
659 		die(STATE_UNKNOWN, "%s\n", _("Cannot create temporary filename"));
660 	}
661 
662 	fp=(FILE *)fdopen(fd,"w");
663 	if(!fp) {
664 		close(fd);
665 		unlink(temp_file);
666 		np_free(temp_file);
667 		die(STATE_UNKNOWN, "%s\n", _("Unable to open temporary state file"));
668 	}
669 
670 	fprintf(fp,"# NP State file\n");
671 	fprintf(fp,"%d\n",NP_STATE_FORMAT_VERSION);
672 	fprintf(fp,"%d\n",this_nagios_plugin->state->data_version);
673 	fprintf(fp,"%lu\n",current_time);
674 	fprintf(fp,"%s\n",data_string);
675 
676 	fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
677 
678 	fflush(fp);
679 
680 	result=fclose(fp);
681 
682 	fsync(fd);
683 
684 	if(result) {
685 		unlink(temp_file);
686 		np_free(temp_file);
687 		die(STATE_UNKNOWN, "%s\n", _("Error writing temp file"));
688 	}
689 
690 	if(rename(temp_file, this_nagios_plugin->state->_filename)) {
691 		unlink(temp_file);
692 		np_free(temp_file);
693 		die(STATE_UNKNOWN, "%s\n", _("Cannot rename state temp file"));
694 	}
695 
696 	np_free(temp_file);
697 }
698