1 /*****************************************************************************
2  *
3  * UTILS.C - Miscellaneous utility functions for Nagios
4  *
5  * Copyright (c) 1999-2009 Ethan Galstad (egalstad@nagios.org)
6  * Last Modified: 06-16-2009
7  *
8  * License:
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  *****************************************************************************/
24 
25 #include "../include/config.h"
26 #include "../include/common.h"
27 #include "../include/objects.h"
28 #include "../include/statusdata.h"
29 #include "../include/comments.h"
30 #include "../include/macros.h"
31 #include "../include/nagios.h"
32 #include "../include/netutils.h"
33 #include "../include/broker.h"
34 #include "../include/nebmods.h"
35 #include "../include/nebmodules.h"
36 
37 
38 #ifdef EMBEDDEDPERL
39 #include "../include/epn_nagios.h"
40 static PerlInterpreter *my_perl = NULL;
41 int use_embedded_perl = TRUE;
42 #endif
43 
44 extern char	*config_file;
45 extern char	*log_file;
46 extern char     *command_file;
47 extern char     *temp_file;
48 extern char     *temp_path;
49 extern char     *check_result_path;
50 extern char     *check_result_path;
51 extern char     *lock_file;
52 extern char	*log_archive_path;
53 extern char     *auth_file;
54 extern char	*p1_file;
55 
56 extern char *xodtemplate_cache_file;
57 extern char *xodtemplate_precache_file;
58 extern char *xsddefault_status_log;
59 extern char *xrddefault_retention_file;
60 extern char *xpddefault_host_perfdata_file;
61 extern char *xpddefault_service_perfdata_file;
62 
63 extern char     *nagios_user;
64 extern char     *nagios_group;
65 
66 extern char     *macro_x_names[MACRO_X_COUNT];
67 extern char     *macro_user[MAX_USER_MACROS];
68 extern customvariablesmember *macro_custom_host_vars;
69 extern customvariablesmember *macro_custom_service_vars;
70 extern customvariablesmember *macro_custom_contact_vars;
71 
72 extern host         *macro_host_ptr;
73 extern hostgroup    *macro_hostgroup_ptr;
74 extern service      *macro_service_ptr;
75 extern servicegroup *macro_servicegroup_ptr;
76 extern contact      *macro_contact_ptr;
77 extern contactgroup *macro_contactgroup_ptr;
78 
79 extern char     *global_host_event_handler;
80 extern char     *global_service_event_handler;
81 extern command  *global_host_event_handler_ptr;
82 extern command  *global_service_event_handler_ptr;
83 
84 extern char     *ocsp_command;
85 extern char     *ochp_command;
86 extern command  *ocsp_command_ptr;
87 extern command  *ochp_command_ptr;
88 
89 extern char     *illegal_object_chars;
90 extern char     *illegal_output_chars;
91 
92 extern int      use_regexp_matches;
93 extern int      use_true_regexp_matching;
94 
95 extern int      sigshutdown;
96 extern int      sigrestart;
97 extern char     *sigs[35];
98 extern int      caught_signal;
99 extern int      sig_id;
100 
101 extern int      daemon_mode;
102 extern int      daemon_dumps_core;
103 
104 extern int      nagios_pid;
105 
106 extern int	use_syslog;
107 extern int      log_notifications;
108 extern int      log_service_retries;
109 extern int      log_host_retries;
110 extern int      log_event_handlers;
111 extern int      log_external_commands;
112 extern int      log_passive_checks;
113 
114 extern unsigned long      logging_options;
115 extern unsigned long      syslog_options;
116 
117 extern int      service_check_timeout;
118 extern int      service_check_timeout_state;
119 extern int      host_check_timeout;
120 extern int      event_handler_timeout;
121 extern int      notification_timeout;
122 extern int      ocsp_timeout;
123 extern int      ochp_timeout;
124 
125 extern int      log_initial_states;
126 
127 extern double   sleep_time;
128 extern int      interval_length;
129 extern int      service_inter_check_delay_method;
130 extern int      host_inter_check_delay_method;
131 extern int      service_interleave_factor_method;
132 extern int      max_host_check_spread;
133 extern int      max_service_check_spread;
134 
135 extern int      command_check_interval;
136 extern int      check_reaper_interval;
137 extern int      max_check_reaper_time;
138 extern int      service_freshness_check_interval;
139 extern int      host_freshness_check_interval;
140 extern int      auto_rescheduling_interval;
141 extern int      auto_rescheduling_window;
142 
143 extern int      check_external_commands;
144 extern int      check_orphaned_services;
145 extern int      check_orphaned_hosts;
146 extern int      check_service_freshness;
147 extern int      check_host_freshness;
148 extern int      auto_reschedule_checks;
149 
150 extern int      additional_freshness_latency;
151 
152 extern int      check_for_updates;
153 extern int      bare_update_check;
154 extern time_t   last_update_check;
155 extern unsigned long update_uid;
156 extern char     *last_program_version;
157 extern int      update_available;
158 extern char     *last_program_version;
159 extern char     *new_program_version;
160 
161 extern int      use_aggressive_host_checking;
162 extern unsigned long cached_host_check_horizon;
163 extern unsigned long cached_service_check_horizon;
164 extern int      enable_predictive_host_dependency_checks;
165 extern int      enable_predictive_service_dependency_checks;
166 
167 extern int      soft_state_dependencies;
168 
169 extern int      retain_state_information;
170 extern int      retention_update_interval;
171 extern int      use_retained_program_state;
172 extern int      use_retained_scheduling_info;
173 extern int      retention_scheduling_horizon;
174 extern unsigned long modified_host_process_attributes;
175 extern unsigned long modified_service_process_attributes;
176 extern unsigned long retained_host_attribute_mask;
177 extern unsigned long retained_service_attribute_mask;
178 extern unsigned long retained_contact_host_attribute_mask;
179 extern unsigned long retained_contact_service_attribute_mask;
180 extern unsigned long retained_process_host_attribute_mask;
181 extern unsigned long retained_process_service_attribute_mask;
182 
183 extern unsigned long next_comment_id;
184 extern unsigned long next_downtime_id;
185 extern unsigned long next_event_id;
186 extern unsigned long next_notification_id;
187 
188 extern int      log_rotation_method;
189 
190 extern time_t   program_start;
191 
192 extern time_t   last_command_check;
193 extern time_t	last_command_status_update;
194 extern time_t   last_log_rotation;
195 
196 extern int      verify_config;
197 extern int      test_scheduling;
198 
199 extern check_result check_result_info;
200 
201 extern int      max_parallel_service_checks;
202 extern int      currently_running_service_checks;
203 
204 extern int      enable_notifications;
205 extern int      execute_service_checks;
206 extern int      accept_passive_service_checks;
207 extern int      execute_host_checks;
208 extern int      accept_passive_host_checks;
209 extern int      enable_event_handlers;
210 extern int      obsess_over_services;
211 extern int      obsess_over_hosts;
212 extern int      enable_failure_prediction;
213 extern int      process_performance_data;
214 
215 extern int      translate_passive_host_checks;
216 extern int      passive_host_checks_are_soft;
217 
218 extern int      aggregate_status_updates;
219 extern int      status_update_interval;
220 
221 extern int      time_change_threshold;
222 
223 extern unsigned long event_broker_options;
224 
225 extern int      process_performance_data;
226 
227 extern int      enable_flap_detection;
228 
229 extern double   low_service_flap_threshold;
230 extern double   high_service_flap_threshold;
231 extern double   low_host_flap_threshold;
232 extern double   high_host_flap_threshold;
233 
234 extern int      use_large_installation_tweaks;
235 extern int      enable_environment_macros;
236 extern int      free_child_process_memory;
237 extern int      child_processes_fork_twice;
238 
239 extern int      enable_embedded_perl;
240 extern int      use_embedded_perl_implicitly;
241 
242 extern int      date_format;
243 
244 extern contact		*contact_list;
245 extern contactgroup	*contactgroup_list;
246 extern host             *host_list;
247 extern hostgroup	*hostgroup_list;
248 extern service          *service_list;
249 extern servicegroup     *servicegroup_list;
250 extern timed_event      *event_list_high;
251 extern timed_event      *event_list_low;
252 extern notification     *notification_list;
253 extern command          *command_list;
254 extern timeperiod       *timeperiod_list;
255 
256 extern int      command_file_fd;
257 extern FILE     *command_file_fp;
258 extern int      command_file_created;
259 
260 #ifdef HAVE_TZNAME
261 #ifdef CYGWIN
262 extern char     *_tzname[2] __declspec(dllimport);
263 #else
264 extern char     *tzname[2];
265 #endif
266 #endif
267 
268 extern check_result    *check_result_list;
269 extern unsigned long   max_check_result_file_age;
270 
271 extern dbuf            check_result_dbuf;
272 
273 extern pthread_t       worker_threads[TOTAL_WORKER_THREADS];
274 extern circular_buffer external_command_buffer;
275 extern circular_buffer check_result_buffer;
276 extern circular_buffer event_broker_buffer;
277 extern int             external_command_buffer_slots;
278 
279 extern check_stats     check_statistics[MAX_CHECK_STATS_TYPES];
280 
281 extern char            *debug_file;
282 extern int             debug_level;
283 extern int             debug_verbosity;
284 extern unsigned long   max_debug_file_size;
285 
286 /* from GNU defines errno as a macro, since it's a per-thread variable */
287 #ifndef errno
288 extern int errno;
289 #endif
290 
291 
292 static long long check_file_size(char *, unsigned long, struct rlimit);
293 
294 /******************************************************************/
295 /******************** SYSTEM COMMAND FUNCTIONS ********************/
296 /******************************************************************/
297 
298 
299 /* executes a system command - used for notifications, event handlers, etc. */
my_system_r(nagios_macros * mac,char * cmd,int timeout,int * early_timeout,double * exectime,char ** output,int max_output_length)300 int my_system_r(nagios_macros *mac, char *cmd, int timeout, int *early_timeout, double *exectime, char **output, int max_output_length) {
301 	pid_t pid = 0;
302 	int status = 0;
303 	int result = 0;
304 	char buffer[MAX_INPUT_BUFFER] = "";
305 	char *temp_buffer = NULL;
306 	int fd[2];
307 	FILE *fp = NULL;
308 	int bytes_read = 0;
309 	struct timeval start_time, end_time;
310 	dbuf output_dbuf;
311 	int dbuf_chunk = 1024;
312 	int flags;
313 #ifdef EMBEDDEDPERL
314 	char fname[512] = "";
315 	char *args[5] = {"", DO_CLEAN, "", "", NULL };
316 	SV *plugin_hndlr_cr = NULL;
317 	char *perl_output = NULL;
318 	int count;
319 	int use_epn = FALSE;
320 #ifdef aTHX
321 	dTHX;
322 #endif
323 	dSP;
324 #endif
325 
326 
327 	log_debug_info(DEBUGL_FUNCTIONS, 0, "my_system_r()\n");
328 
329 	/* initialize return variables */
330 	if(output != NULL)
331 		*output = NULL;
332 	*early_timeout = FALSE;
333 	*exectime = 0.0;
334 
335 	/* if no command was passed, return with no error */
336 	if(cmd == NULL)
337 		return STATE_OK;
338 
339 	log_debug_info(DEBUGL_COMMANDS, 1, "Running command '%s'...\n", cmd);
340 
341 #ifdef EMBEDDEDPERL
342 
343 	/* get"filename" component of command */
344 	strncpy(fname, cmd, strcspn(cmd, " "));
345 	fname[strcspn(cmd, " ")] = '\x0';
346 
347 	/* should we use the embedded Perl interpreter to run this script? */
348 	use_epn = file_uses_embedded_perl(fname);
349 
350 	/* if yes, do some initialization */
351 	if(use_epn == TRUE) {
352 
353 		args[0] = fname;
354 		args[2] = "";
355 
356 		if(strchr(cmd, ' ') == NULL)
357 			args[3] = "";
358 		else
359 			args[3] = cmd + strlen(fname) + 1;
360 
361 		/* call our perl interpreter to compile and optionally cache the compiled script. */
362 
363 		ENTER;
364 		SAVETMPS;
365 		PUSHMARK(SP);
366 
367 		XPUSHs(sv_2mortal(newSVpv(args[0], 0)));
368 		XPUSHs(sv_2mortal(newSVpv(args[1], 0)));
369 		XPUSHs(sv_2mortal(newSVpv(args[2], 0)));
370 		XPUSHs(sv_2mortal(newSVpv(args[3], 0)));
371 
372 		PUTBACK;
373 
374 		call_pv("Embed::Persistent::eval_file", G_EVAL);
375 
376 		SPAGAIN;
377 
378 		if(SvTRUE(ERRSV)) {
379 			/*
380 			 * XXXX need pipe open to send the compilation failure message back to Nagios ?
381 			 */
382 			(void) POPs ;
383 
384 			asprintf(&temp_buffer, "%s", SvPVX(ERRSV));
385 
386 			log_debug_info(DEBUGL_COMMANDS, 0, "Embedded perl failed to compile %s, compile error %s\n", fname, temp_buffer);
387 
388 			logit(NSLOG_RUNTIME_WARNING, TRUE, "%s\n", temp_buffer);
389 
390 			my_free(temp_buffer);
391 
392 			return STATE_UNKNOWN;
393 			}
394 		else {
395 			plugin_hndlr_cr = newSVsv(POPs);
396 
397 			log_debug_info(DEBUGL_COMMANDS, 0, "Embedded perl successfully compiled %s and returned plugin handler (Perl subroutine code ref)\n", fname);
398 
399 			PUTBACK ;
400 			FREETMPS ;
401 			LEAVE ;
402 			}
403 		}
404 #endif
405 
406 	/* create a pipe */
407 	pipe(fd);
408 
409 	/* make the pipe non-blocking */
410 	fcntl(fd[0], F_SETFL, O_NONBLOCK);
411 	fcntl(fd[1], F_SETFL, O_NONBLOCK);
412 
413 	/* get the command start time */
414 	gettimeofday(&start_time, NULL);
415 
416 #ifdef USE_EVENT_BROKER
417 	/* send data to event broker */
418 	end_time.tv_sec = 0L;
419 	end_time.tv_usec = 0L;
420 	broker_system_command(NEBTYPE_SYSTEM_COMMAND_START, NEBFLAG_NONE, NEBATTR_NONE, start_time, end_time, *exectime, timeout, *early_timeout, result, cmd, NULL, NULL);
421 #endif
422 
423 	/* fork */
424 	pid = fork();
425 
426 	/* return an error if we couldn't fork */
427 	if(pid == -1) {
428 		logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: fork() in my_system_r() failed for command \"%s\"\n", cmd);
429 
430 		/* close both ends of the pipe */
431 		close(fd[0]);
432 		close(fd[1]);
433 
434 		return STATE_UNKNOWN;
435 		}
436 
437 	/* execute the command in the child process */
438 	if(pid == 0) {
439 
440 		/* become process group leader */
441 		setpgid(0, 0);
442 
443 		/* set environment variables */
444 		set_all_macro_environment_vars_r(mac, TRUE);
445 
446 		/* ADDED 11/12/07 EG */
447 		/* close external command file and shut down worker thread */
448 		close_command_file();
449 
450 		/* reset signal handling */
451 		reset_sighandler();
452 
453 		/* close pipe for reading */
454 		close(fd[0]);
455 
456 		/* prevent fd from being inherited by child processed */
457 		flags = fcntl(fd[1], F_GETFD, 0);
458 		flags |= FD_CLOEXEC;
459 		fcntl(fd[1], F_SETFD, flags);
460 
461 		/* trap commands that timeout */
462 		signal(SIGALRM, my_system_sighandler);
463 		alarm(timeout);
464 
465 
466 		/******** BEGIN EMBEDDED PERL CODE EXECUTION ********/
467 
468 #ifdef EMBEDDEDPERL
469 		if(use_epn == TRUE) {
470 
471 			/* execute our previously compiled script - by call_pv("Embed::Persistent::eval_file",..) */
472 			ENTER;
473 			SAVETMPS;
474 			PUSHMARK(SP);
475 
476 			XPUSHs(sv_2mortal(newSVpv(args[0], 0)));
477 			XPUSHs(sv_2mortal(newSVpv(args[1], 0)));
478 			XPUSHs(plugin_hndlr_cr);
479 			XPUSHs(sv_2mortal(newSVpv(args[3], 0)));
480 
481 			PUTBACK;
482 
483 			count = call_pv("Embed::Persistent::run_package", G_ARRAY);
484 			/* count is a debug hook. It should always be two (2), because the persistence framework tries to return two (2) args */
485 
486 			SPAGAIN;
487 
488 			perl_output = POPpx ;
489 			strip(perl_output);
490 			strncpy(buffer, (perl_output == NULL) ? "" : perl_output, sizeof(buffer));
491 			buffer[sizeof(buffer) - 1] = '\x0';
492 			status = POPi ;
493 
494 			PUTBACK;
495 			FREETMPS;
496 			LEAVE;
497 
498 			log_debug_info(DEBUGL_COMMANDS, 0, "Embedded perl ran command %s with output %d, %s\n", fname, status, buffer);
499 
500 			/* write the output back to the parent process */
501 			write(fd[1], buffer, strlen(buffer) + 1);
502 
503 			/* close pipe for writing */
504 			close(fd[1]);
505 
506 			/* reset the alarm */
507 			alarm(0);
508 
509 			_exit(status);
510 			}
511 #endif
512 		/******** END EMBEDDED PERL CODE EXECUTION ********/
513 
514 
515 		/* run the command */
516 		fp = (FILE *)popen(cmd, "r");
517 
518 		/* report an error if we couldn't run the command */
519 		if(fp == NULL) {
520 
521 			strncpy(buffer, "(Error: Could not execute command)\n", sizeof(buffer) - 1);
522 			buffer[sizeof(buffer) - 1] = '\x0';
523 
524 			/* write the error back to the parent process */
525 			write(fd[1], buffer, strlen(buffer) + 1);
526 
527 			result = STATE_CRITICAL;
528 			}
529 		else {
530 
531 			/* write all the lines of output back to the parent process */
532 			while(fgets(buffer, sizeof(buffer) - 1, fp))
533 				write(fd[1], buffer, strlen(buffer));
534 
535 			/* close the command and get termination status */
536 			status = pclose(fp);
537 
538 			/* report an error if we couldn't close the command */
539 			if(status == -1)
540 				result = STATE_CRITICAL;
541 			else {
542 				if(WEXITSTATUS(status) == 0 && WIFSIGNALED(status))
543 					result = 128 + WTERMSIG(status);
544 				result = WEXITSTATUS(status);
545 				}
546 			}
547 
548 		/* close pipe for writing */
549 		close(fd[1]);
550 
551 		/* reset the alarm */
552 		alarm(0);
553 
554 		/* clear environment variables */
555 		set_all_macro_environment_vars_r(mac, FALSE);
556 
557 #ifndef DONT_USE_MEMORY_PERFORMANCE_TWEAKS
558 		/* free allocated memory */
559 		/* this needs to be done last, so we don't free memory for variables before they're used above */
560 		if(free_child_process_memory == TRUE)
561 			free_memory(mac);
562 #endif
563 
564 		_exit(result);
565 		}
566 
567 	/* parent waits for child to finish executing command */
568 	else {
569 
570 		/* close pipe for writing */
571 		close(fd[1]);
572 
573 		/* wait for child to exit */
574 		waitpid(pid, &status, 0);
575 
576 		/* get the end time for running the command */
577 		gettimeofday(&end_time, NULL);
578 
579 		/* return execution time in milliseconds */
580 		*exectime = (double)((double)(end_time.tv_sec - start_time.tv_sec) + (double)((end_time.tv_usec - start_time.tv_usec) / 1000) / 1000.0);
581 		if(*exectime < 0.0)
582 			*exectime = 0.0;
583 
584 		/* get the exit code returned from the program */
585 		result = WEXITSTATUS(status);
586 
587 		/* check for possibly missing scripts/binaries/etc */
588 		if(result == 126 || result == 127) {
589 			temp_buffer = "\163\157\151\147\141\156\040\145\144\151\163\156\151";
590 			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Attempting to execute the command \"%s\" resulted in a return code of %d.  Make sure the script or binary you are trying to execute actually exists...\n", cmd, result);
591 			}
592 
593 		/* check bounds on the return value */
594 		if(result < -1 || result > 3)
595 			result = STATE_UNKNOWN;
596 
597 		/* initialize dynamic buffer */
598 		dbuf_init(&output_dbuf, dbuf_chunk);
599 
600 		/* Opsera patch to check timeout before attempting to read output via pipe. Originally by Sven Nierlein */
601 		/* if there was a critical return code AND the command time exceeded the timeout thresholds, assume a timeout */
602 		if(result == STATE_CRITICAL && (end_time.tv_sec - start_time.tv_sec) >= timeout) {
603 
604 			/* set the early timeout flag */
605 			*early_timeout = TRUE;
606 
607 			/* try to kill the command that timed out by sending termination signal to child process group */
608 			kill((pid_t)(-pid), SIGTERM);
609 			sleep(1);
610 			kill((pid_t)(-pid), SIGKILL);
611 			}
612 
613 		/* read output if timeout has not occurred */
614 		else {
615 
616 			/* initialize output */
617 			strcpy(buffer, "");
618 
619 			/* try and read the results from the command output (retry if we encountered a signal) */
620 			do {
621 				bytes_read = read(fd[0], buffer, sizeof(buffer) - 1);
622 
623 				/* append data we just read to dynamic buffer */
624 				if(bytes_read > 0) {
625 					buffer[bytes_read] = '\x0';
626 					dbuf_strcat(&output_dbuf, buffer);
627 					}
628 
629 				/* handle errors */
630 				if(bytes_read == -1) {
631 					/* we encountered a recoverable error, so try again */
632 					if(errno == EINTR)
633 						continue;
634 					/* patch by Henning Brauer to prevent CPU hogging */
635 					else if(errno == EAGAIN) {
636 						struct pollfd pfd;
637 
638 						pfd.fd = fd[0];
639 						pfd.events = POLLIN;
640 						poll(&pfd, 1, -1);
641 						continue;
642 						}
643 					else
644 						break;
645 					}
646 
647 				/* we're done */
648 				if(bytes_read == 0)
649 					break;
650 
651 				}
652 			while(1);
653 
654 			/* cap output length - this isn't necessary, but it keeps runaway plugin output from causing problems */
655 			if(max_output_length > 0  && output_dbuf.used_size > max_output_length)
656 				output_dbuf.buf[max_output_length] = '\x0';
657 
658 			if(output != NULL && output_dbuf.buf)
659 				*output = (char *)strdup(output_dbuf.buf);
660 
661 			}
662 
663 		log_debug_info(DEBUGL_COMMANDS, 1, "Execution time=%.3f sec, early timeout=%d, result=%d, output=%s\n", *exectime, *early_timeout, result, (output_dbuf.buf == NULL) ? "(null)" : output_dbuf.buf);
664 
665 #ifdef USE_EVENT_BROKER
666 		/* send data to event broker */
667 		broker_system_command(NEBTYPE_SYSTEM_COMMAND_END, NEBFLAG_NONE, NEBATTR_NONE, start_time, end_time, *exectime, timeout, *early_timeout, result, cmd, (output_dbuf.buf == NULL) ? NULL : output_dbuf.buf, NULL);
668 #endif
669 
670 		/* free memory */
671 		dbuf_free(&output_dbuf);
672 
673 		/* close the pipe for reading */
674 		close(fd[0]);
675 		}
676 
677 	return result;
678 	}
679 
680 /*
681  * For API compatibility, we must include a my_system() whose
682  * signature doesn't include the nagios_macros variable.
683  * NDOUtils uses this. Possibly other modules as well.
684  */
my_system(char * cmd,int timeout,int * early_timeout,double * exectime,char ** output,int max_output_length)685 int my_system(char *cmd, int timeout, int *early_timeout, double *exectime, char **output, int max_output_length) {
686 	return my_system_r(get_global_macros(), cmd, timeout, early_timeout, exectime, output, max_output_length);
687 	}
688 
689 
690 /* given a "raw" command, return the "expanded" or "whole" command line */
get_raw_command_line_r(nagios_macros * mac,command * cmd_ptr,char * cmd,char ** full_command,int macro_options)691 int get_raw_command_line_r(nagios_macros *mac, command *cmd_ptr, char *cmd, char **full_command, int macro_options) {
692 	char temp_arg[MAX_COMMAND_BUFFER] = "";
693 	char *arg_buffer = NULL;
694 	register int x = 0;
695 	register int y = 0;
696 	register int arg_index = 0;
697 	register int escaped = FALSE;
698 
699 	log_debug_info(DEBUGL_FUNCTIONS, 0, "get_raw_command_line_r()\n");
700 
701 	/* clear the argv macros */
702 	clear_argv_macros_r(mac);
703 
704 	/* make sure we've got all the requirements */
705 	if(cmd_ptr == NULL || full_command == NULL)
706 		return ERROR;
707 
708 	log_debug_info(DEBUGL_COMMANDS | DEBUGL_CHECKS | DEBUGL_MACROS, 2, "Raw Command Input: %s\n", cmd_ptr->command_line);
709 
710 	/* get the full command line */
711 	*full_command = (char *)strdup((cmd_ptr->command_line == NULL) ? "" : cmd_ptr->command_line);
712 
713 	/* XXX: Crazy indent */
714 	/* get the command arguments */
715 	if(cmd != NULL) {
716 
717 		/* skip the command name (we're about to get the arguments)... */
718 		for(arg_index = 0;; arg_index++) {
719 			if(cmd[arg_index] == '!' || cmd[arg_index] == '\x0')
720 				break;
721 			}
722 
723 		/* get each command argument */
724 		for(x = 0; x < MAX_COMMAND_ARGUMENTS; x++) {
725 
726 			/* we reached the end of the arguments... */
727 			if(cmd[arg_index] == '\x0')
728 				break;
729 
730 			/* get the next argument */
731 			/* can't use strtok(), as that's used in process_macros... */
732 			for(arg_index++, y = 0; y < sizeof(temp_arg) - 1; arg_index++) {
733 
734 				/* backslashes escape */
735 				if(cmd[arg_index] == '\\' && escaped == FALSE) {
736 					escaped = TRUE;
737 					continue;
738 					}
739 
740 				/* end of argument */
741 				if((cmd[arg_index] == '!' && escaped == FALSE) || cmd[arg_index] == '\x0')
742 					break;
743 
744 				/* normal of escaped char */
745 				temp_arg[y] = cmd[arg_index];
746 				y++;
747 
748 				/* clear escaped flag */
749 				escaped = FALSE;
750 				}
751 			temp_arg[y] = '\x0';
752 
753 			/* ADDED 01/29/04 EG */
754 			/* process any macros we find in the argument */
755 			process_macros_r(mac, temp_arg, &arg_buffer, macro_options);
756 
757 			mac->argv[x] = arg_buffer;
758 			}
759 		}
760 
761 	log_debug_info(DEBUGL_COMMANDS | DEBUGL_CHECKS | DEBUGL_MACROS, 2, "Expanded Command Output: %s\n", *full_command);
762 
763 	return OK;
764 	}
765 
766 /*
767  * This function modifies the global macro struct and is thus not
768  * threadsafe
769  */
get_raw_command_line(command * cmd_ptr,char * cmd,char ** full_command,int macro_options)770 int get_raw_command_line(command *cmd_ptr, char *cmd, char **full_command, int macro_options) {
771 	nagios_macros *mac;
772 
773 	mac = get_global_macros();
774 	return get_raw_command_line_r(mac, cmd_ptr, cmd, full_command, macro_options);
775 	}
776 
777 
778 
779 /******************************************************************/
780 /******************** ENVIRONMENT FUNCTIONS ***********************/
781 /******************************************************************/
782 
783 /* sets or unsets an environment variable */
set_environment_var(char * name,char * value,int set)784 int set_environment_var(char *name, char *value, int set) {
785 #ifndef HAVE_SETENV
786 	char *env_string = NULL;
787 #endif
788 
789 	/* we won't mess with null variable names */
790 	if(name == NULL)
791 		return ERROR;
792 
793 	/* set the environment variable */
794 	if(set == TRUE) {
795 
796 #ifdef HAVE_SETENV
797 		setenv(name, (value == NULL) ? "" : value, 1);
798 #else
799 		/* needed for Solaris and systems that don't have setenv() */
800 		/* this will leak memory, but in a "controlled" way, since lost memory should be freed when the child process exits */
801 		asprintf(&env_string, "%s=%s", name, (value == NULL) ? "" : value);
802 		if(env_string)
803 			putenv(env_string);
804 #endif
805 		}
806 	/* clear the variable */
807 	else {
808 #ifdef HAVE_UNSETENV
809 		unsetenv(name);
810 #endif
811 		}
812 
813 	return OK;
814 	}
815 
816 
817 
818 
819 /******************************************************************/
820 /************************* TIME FUNCTIONS *************************/
821 /******************************************************************/
822 
823 
824 /* Checks if the given time is in daylight time saving period */
is_dst_time(time_t * time)825 int is_dst_time(time_t *time) {
826 	struct tm *bt = localtime(time);
827 	return bt->tm_isdst;
828 	}
829 
830 /* Returns the shift in seconds if the given times are across the daylight time saving period change */
get_dst_shift(time_t * start,time_t * end)831 int get_dst_shift(time_t *start, time_t *end) {
832 	int shift = 0, dst_end, dst_start;
833 	dst_start = is_dst_time(start);
834 	dst_end = is_dst_time(end);
835 	if(dst_start < dst_end) {
836 		shift = 3600;
837 		}
838 	else if(dst_start > dst_end) {
839 		shift = -3600;
840 		}
841 	return shift;
842 	}
843 
844 /*#define TEST_TIMEPERIODS_A 1*/
845 
846 /* see if the specified time falls into a valid time range in the given time period */
check_time_against_period(time_t test_time,timeperiod * tperiod)847 int check_time_against_period(time_t test_time, timeperiod *tperiod) {
848 	timeperiodexclusion *temp_timeperiodexclusion = NULL;
849 	timeperiodexclusion *first_timeperiodexclusion = NULL;
850 	daterange *temp_daterange = NULL;
851 	timerange *temp_timerange = NULL;
852 	time_t midnight = (time_t)0L;
853 	time_t start_time = (time_t)0L;
854 	time_t end_time = (time_t)0L;
855 	int found_match = FALSE;
856 	struct tm *t, tm_s;
857 	int daterange_type = 0;
858 	unsigned long days = 0L;
859 	time_t day_range_start = (time_t)0L;
860 	time_t day_range_end = (time_t)0L;
861 	int test_time_year = 0;
862 	int test_time_mon = 0;
863 	int test_time_mday = 0;
864 	int test_time_wday = 0;
865 	int year = 0;
866 	int shift;
867 
868 	log_debug_info(DEBUGL_FUNCTIONS, 0, "check_time_against_period()\n");
869 
870 	/* if no period was specified, assume the time is good */
871 	if(tperiod == NULL)
872 		return OK;
873 
874 	/* test exclusions first - if exclusions match current time, bail out with an error */
875 	/* clear exclusions list before recursing (and restore afterwards) to prevent endless loops... */
876 	first_timeperiodexclusion = tperiod->exclusions;
877 	tperiod->exclusions = NULL;
878 	for(temp_timeperiodexclusion = first_timeperiodexclusion; temp_timeperiodexclusion != NULL; temp_timeperiodexclusion = temp_timeperiodexclusion->next) {
879 		if(check_time_against_period(test_time, temp_timeperiodexclusion->timeperiod_ptr) == OK) {
880 			tperiod->exclusions = first_timeperiodexclusion;
881 			return ERROR;
882 			}
883 		}
884 	tperiod->exclusions = first_timeperiodexclusion;
885 
886 	/* save values for later */
887 	t = localtime_r((time_t *)&test_time, &tm_s);
888 	test_time_year = t->tm_year;
889 	test_time_mon = t->tm_mon;
890 	test_time_mday = t->tm_mday;
891 	test_time_wday = t->tm_wday;
892 
893 	/* calculate the start of the day (midnight, 00:00 hours) when the specified test time occurs */
894 	t->tm_sec = 0;
895 	t->tm_min = 0;
896 	t->tm_hour = 0;
897 	midnight = mktime(t);
898 
899 	/**** check exceptions first ****/
900 	for(daterange_type = 0; daterange_type < DATERANGE_TYPES; daterange_type++) {
901 
902 		for(temp_daterange = tperiod->exceptions[daterange_type]; temp_daterange != NULL; temp_daterange = temp_daterange->next) {
903 
904 #ifdef TEST_TIMEPERIODS_A
905 			printf("TYPE: %d\n", daterange_type);
906 			printf("TEST:     %lu = %s", (unsigned long)test_time, ctime(&test_time));
907 			printf("MIDNIGHT: %lu = %s", (unsigned long)midnight, ctime(&midnight));
908 #endif
909 
910 			/* get the start time */
911 			switch(daterange_type) {
912 				case DATERANGE_CALENDAR_DATE:
913 					t->tm_sec = 0;
914 					t->tm_min = 0;
915 					t->tm_hour = 0;
916 					t->tm_wday = 0;
917 					t->tm_mday = temp_daterange->smday;
918 					t->tm_mon = temp_daterange->smon;
919 					t->tm_year = (temp_daterange->syear - 1900);
920 					t->tm_isdst = -1;
921 					start_time = mktime(t);
922 					break;
923 				case DATERANGE_MONTH_DATE:
924 					start_time = calculate_time_from_day_of_month(test_time_year, temp_daterange->smon, temp_daterange->smday);
925 					break;
926 				case DATERANGE_MONTH_DAY:
927 					start_time = calculate_time_from_day_of_month(test_time_year, test_time_mon, temp_daterange->smday);
928 					break;
929 				case DATERANGE_MONTH_WEEK_DAY:
930 					start_time = calculate_time_from_weekday_of_month(test_time_year, temp_daterange->smon, temp_daterange->swday, temp_daterange->swday_offset);
931 					break;
932 				case DATERANGE_WEEK_DAY:
933 					start_time = calculate_time_from_weekday_of_month(test_time_year, test_time_mon, temp_daterange->swday, temp_daterange->swday_offset);
934 					break;
935 				default:
936 					continue;
937 					break;
938 				}
939 
940 			/* get the end time */
941 			switch(daterange_type) {
942 				case DATERANGE_CALENDAR_DATE:
943 					t->tm_sec = 0;
944 					t->tm_min = 0;
945 					t->tm_hour = 0;
946 					t->tm_wday = 0;
947 					t->tm_mday = temp_daterange->emday;
948 					t->tm_mon = temp_daterange->emon;
949 					t->tm_year = (temp_daterange->eyear - 1900);
950 					t->tm_isdst = -1;
951 					end_time = mktime(t);
952 					break;
953 				case DATERANGE_MONTH_DATE:
954 					year = test_time_year;
955 					end_time = calculate_time_from_day_of_month(year, temp_daterange->emon, temp_daterange->emday);
956 					/* advance a year if necessary: august 2 - february 5 */
957 					if(end_time < start_time) {
958 						year++;
959 						end_time = calculate_time_from_day_of_month(year, temp_daterange->emon, temp_daterange->emday);
960 						}
961 					break;
962 				case DATERANGE_MONTH_DAY:
963 					end_time = calculate_time_from_day_of_month(test_time_year, test_time_mon, temp_daterange->emday);
964 					break;
965 				case DATERANGE_MONTH_WEEK_DAY:
966 					year = test_time_year;
967 					end_time = calculate_time_from_weekday_of_month(year, temp_daterange->emon, temp_daterange->ewday, temp_daterange->ewday_offset);
968 					/* advance a year if necessary: thursday 2 august - monday 3 february */
969 					if(end_time < start_time) {
970 						year++;
971 						end_time = calculate_time_from_weekday_of_month(year, temp_daterange->emon, temp_daterange->ewday, temp_daterange->ewday_offset);
972 						}
973 					break;
974 				case DATERANGE_WEEK_DAY:
975 					end_time = calculate_time_from_weekday_of_month(test_time_year, test_time_mon, temp_daterange->ewday, temp_daterange->ewday_offset);
976 					break;
977 				default:
978 					continue;
979 					break;
980 				}
981 
982 #ifdef TEST_TIMEPERIODS_A
983 			printf("START:    %lu = %s", (unsigned long)start_time, ctime(&start_time));
984 			printf("END:      %lu = %s", (unsigned long)end_time, ctime(&end_time));
985 #endif
986 
987 			/* start date was bad, so skip this date range */
988 			if((unsigned long)start_time == 0L)
989 				continue;
990 
991 			/* end date was bad - see if we can handle the error */
992 			if((unsigned long)end_time == 0L) {
993 				switch(daterange_type) {
994 					case DATERANGE_CALENDAR_DATE:
995 						continue;
996 						break;
997 					case DATERANGE_MONTH_DATE:
998 						/* end date can't be helped, so skip it */
999 						if(temp_daterange->emday < 0)
1000 							continue;
1001 
1002 						/* else end date slipped past end of month, so use last day of month as end date */
1003 						/* use same year calculated above */
1004 						end_time = calculate_time_from_day_of_month(year, temp_daterange->emon, -1);
1005 						break;
1006 					case DATERANGE_MONTH_DAY:
1007 						/* end date can't be helped, so skip it */
1008 						if(temp_daterange->emday < 0)
1009 							continue;
1010 
1011 						/* else end date slipped past end of month, so use last day of month as end date */
1012 						end_time = calculate_time_from_day_of_month(test_time_year, test_time_mon, -1);
1013 						break;
1014 					case DATERANGE_MONTH_WEEK_DAY:
1015 						/* end date can't be helped, so skip it */
1016 						if(temp_daterange->ewday_offset < 0)
1017 							continue;
1018 
1019 						/* else end date slipped past end of month, so use last day of month as end date */
1020 						/* use same year calculated above */
1021 						end_time = calculate_time_from_day_of_month(year, test_time_mon, -1);
1022 						break;
1023 					case DATERANGE_WEEK_DAY:
1024 						/* end date can't be helped, so skip it */
1025 						if(temp_daterange->ewday_offset < 0)
1026 							continue;
1027 
1028 						/* else end date slipped past end of month, so use last day of month as end date */
1029 						end_time = calculate_time_from_day_of_month(test_time_year, test_time_mon, -1);
1030 						break;
1031 					default:
1032 						continue;
1033 						break;
1034 					}
1035 				}
1036 
1037 			/* calculate skip date start (and end) */
1038 			if(temp_daterange->skip_interval > 1) {
1039 
1040 				/* skip start date must be before test time */
1041 				if(start_time > test_time)
1042 					continue;
1043 
1044 				/* check if interval is across dlst change and gets the compensation */
1045 				shift = get_dst_shift(&start_time, &midnight);
1046 
1047 				/* how many days have passed between skip start date and test time? */
1048 				days = (shift + (unsigned long)midnight - (unsigned long)start_time) / (3600 * 24);
1049 
1050 				/* if test date doesn't fall on a skip interval day, bail out early */
1051 				if((days % temp_daterange->skip_interval) != 0)
1052 					continue;
1053 
1054 				/* use midnight of test date as start time */
1055 				else
1056 					start_time = midnight;
1057 
1058 				/* if skipping range has no end, use test date as end */
1059 				if((daterange_type == DATERANGE_CALENDAR_DATE) && (is_daterange_single_day(temp_daterange) == TRUE))
1060 					end_time = midnight;
1061 				}
1062 
1063 #ifdef TEST_TIMEPERIODS_A
1064 			printf("NEW START:    %lu = %s", (unsigned long)start_time, ctime(&start_time));
1065 			printf("NEW END:      %lu = %s", (unsigned long)end_time, ctime(&end_time));
1066 			printf("%d DAYS PASSED\n", days);
1067 			printf("DLST SHIFT:   %d", shift);
1068 #endif
1069 
1070 			/* time falls into the range of days */
1071 			if(midnight >= start_time && midnight <= end_time)
1072 				found_match = TRUE;
1073 
1074 			/* found a day match, so see if time ranges are good */
1075 			if(found_match == TRUE) {
1076 
1077 				for(temp_timerange = temp_daterange->times; temp_timerange != NULL; temp_timerange = temp_timerange->next) {
1078 
1079 					/* ranges with start/end of zero mean exlude this day */
1080 					if(temp_timerange->range_start == 0 && temp_timerange->range_end == 0) {
1081 #ifdef TEST_TIMEPERIODS_A
1082 						printf("0 MINUTE RANGE EXCLUSION\n");
1083 #endif
1084 						continue;
1085 						}
1086 
1087 					day_range_start = (time_t)(midnight + temp_timerange->range_start);
1088 					day_range_end = (time_t)(midnight + temp_timerange->range_end);
1089 
1090 #ifdef TEST_TIMEPERIODS_A
1091 					printf("  RANGE START: %lu (%lu) = %s", temp_timerange->range_start, (unsigned long)day_range_start, ctime(&day_range_start));
1092 					printf("  RANGE END:   %lu (%lu) = %s", temp_timerange->range_end, (unsigned long)day_range_end, ctime(&day_range_end));
1093 #endif
1094 
1095 					/* if the user-specified time falls in this range, return with a positive result */
1096 					if(test_time >= day_range_start && test_time <= day_range_end)
1097 						return OK;
1098 					}
1099 
1100 				/* no match, so bail with error */
1101 				return ERROR;
1102 				}
1103 			}
1104 		}
1105 
1106 
1107 	/**** check normal, weekly rotating schedule last ****/
1108 
1109 	/* check weekday time ranges */
1110 	for(temp_timerange = tperiod->days[test_time_wday]; temp_timerange != NULL; temp_timerange = temp_timerange->next) {
1111 
1112 		day_range_start = (time_t)(midnight + temp_timerange->range_start);
1113 		day_range_end = (time_t)(midnight + temp_timerange->range_end);
1114 
1115 		/* if the user-specified time falls in this range, return with a positive result */
1116 		if(test_time >= day_range_start && test_time <= day_range_end)
1117 			return OK;
1118 		}
1119 
1120 	return ERROR;
1121 	}
1122 
1123 
1124 
1125 /*#define TEST_TIMEPERIODS_B 1*/
1126 
1127 /* Separate this out from public get_next_valid_time for testing, so we can mock current_time */
_get_next_valid_time(time_t pref_time,time_t current_time,time_t * valid_time,timeperiod * tperiod)1128 void _get_next_valid_time(time_t pref_time, time_t current_time, time_t *valid_time, timeperiod *tperiod) {
1129 	time_t preferred_time = (time_t)0L;
1130 	timerange *temp_timerange;
1131 	daterange *temp_daterange;
1132 	time_t midnight = (time_t)0L;
1133 	struct tm *t, tm_s;
1134 	time_t day_start = (time_t)0L;
1135 	time_t day_range_start = (time_t)0L;
1136 	time_t day_range_end = (time_t)0L;
1137 	time_t start_time = (time_t)0L;
1138 	time_t end_time = (time_t)0L;
1139 	int have_earliest_time = FALSE;
1140 	time_t earliest_time = (time_t)0L;
1141 	time_t earliest_day = (time_t)0L;
1142 	time_t potential_time = (time_t)0L;
1143 	int weekday = 0;
1144 	int has_looped = FALSE;
1145 	int days_into_the_future = 0;
1146 	int daterange_type = 0;
1147 	unsigned long days = 0L;
1148 	unsigned long advance_interval = 0L;
1149 	int year = 0; /* new */
1150 	int month = 0; /* new */
1151 
1152 	int pref_time_year = 0;
1153 	int pref_time_mon = 0;
1154 	int pref_time_mday = 0;
1155 	int pref_time_wday = 0;
1156 	int current_time_year = 0;
1157 	int current_time_mon = 0;
1158 	int current_time_mday = 0;
1159 	int current_time_wday = 0;
1160 	int shift;
1161 
1162 	/* preferred time must be now or in the future */
1163 	preferred_time = (pref_time < current_time) ? current_time : pref_time;
1164 
1165 	/* if no timeperiod, go with preferred time */
1166 	if(tperiod == NULL) {
1167 		*valid_time = preferred_time;
1168 		return;
1169 		}
1170 
1171 	/* if the preferred time is valid in timeperiod, go with it */
1172 	/* this is necessary because the code below won't catch exceptions where preferred day is last (or only) date in timeperiod (date range) and last valid time has already passed */
1173 	/* performing this check and bailing out early allows us to skip having to check the next instance of a date range exception or weekday to determine the next valid time */
1174 	if(check_time_against_period(preferred_time, tperiod) == OK) {
1175 #ifdef TEST_TIMEPERIODS_B
1176 		printf("PREF TIME IS VALID\n");
1177 #endif
1178 		*valid_time = preferred_time;
1179 		return;
1180 		}
1181 
1182 	/* calculate the start of the day (midnight, 00:00 hours) of preferred time */
1183 	t = localtime_r(&preferred_time, &tm_s);
1184 	t->tm_sec = 0;
1185 	t->tm_min = 0;
1186 	t->tm_hour = 0;
1187 	midnight = mktime(t);
1188 
1189 	/* save pref time values for later */
1190 	pref_time_year = t->tm_year;
1191 	pref_time_mon = t->tm_mon;
1192 	pref_time_mday = t->tm_mday;
1193 	pref_time_wday = t->tm_wday;
1194 
1195 	/* save current time values for later */
1196 	t = localtime_r(&current_time, &tm_s);
1197 	current_time_year = t->tm_year;
1198 	current_time_mon = t->tm_mon;
1199 	current_time_mday = t->tm_mday;
1200 	current_time_wday = t->tm_wday;
1201 
1202 #ifdef TEST_TIMEPERIODS_B
1203 	printf("PREF TIME:    %lu = %s", (unsigned long)preferred_time, ctime(&preferred_time));
1204 	printf("CURRENT TIME: %lu = %s", (unsigned long)current_time, ctime(&current_time));
1205 	printf("PREF YEAR:    %d, MON: %d, MDAY: %d, WDAY: %d\n", pref_time_year, pref_time_mon, pref_time_mday, pref_time_wday);
1206 	printf("CURRENT YEAR: %d, MON: %d, MDAY: %d, WDAY: %d\n", current_time_year, current_time_mon, current_time_mday, current_time_wday);
1207 #endif
1208 
1209 	/**** check exceptions (in this timeperiod definition) first ****/
1210 	for(daterange_type = 0; daterange_type < DATERANGE_TYPES; daterange_type++) {
1211 
1212 #ifdef TEST_TIMEPERIODS_B
1213 		printf("TYPE: %d\n", daterange_type);
1214 #endif
1215 
1216 		for(temp_daterange = tperiod->exceptions[daterange_type]; temp_daterange != NULL; temp_daterange = temp_daterange->next) {
1217 
1218 			/* get the start time */
1219 			switch(daterange_type) {
1220 				case DATERANGE_CALENDAR_DATE: /* 2009-08-11 */
1221 					t->tm_sec = 0;
1222 					t->tm_min = 0;
1223 					t->tm_hour = 0;
1224 					t->tm_mday = temp_daterange->smday;
1225 					t->tm_mon = temp_daterange->smon;
1226 					t->tm_year = (temp_daterange->syear - 1900);
1227 					t->tm_isdst = -1;
1228 					start_time = mktime(t);
1229 					break;
1230 				case DATERANGE_MONTH_DATE:  /* january 1 */
1231 					/* what year should we use? */
1232 					year = (pref_time_year < current_time_year) ? current_time_year : pref_time_year;
1233 					/* advance an additional year if we already passed the end month date */
1234 					if((temp_daterange->emon < current_time_mon) || ((temp_daterange->emon == current_time_mon) && temp_daterange->emday < current_time_mday))
1235 						year++;
1236 					start_time = calculate_time_from_day_of_month(year, temp_daterange->smon, temp_daterange->smday);
1237 					break;
1238 				case DATERANGE_MONTH_DAY:  /* day 3 */
1239 					/* what year should we use? */
1240 					year = (pref_time_year < current_time_year) ? current_time_year : pref_time_year;
1241 					/* use current month */
1242 					month = current_time_mon;
1243 					/* advance an additional month (and possibly the year) if we already passed the end day of month */
1244 					if(temp_daterange->emday < current_time_mday) {
1245 						/*if(month==1){*/
1246 						if(month == 11) {
1247 							month = 0;
1248 							year++;
1249 							}
1250 						else
1251 							month++;
1252 						}
1253 					start_time = calculate_time_from_day_of_month(year, month, temp_daterange->smday);
1254 					break;
1255 				case DATERANGE_MONTH_WEEK_DAY: /* thursday 2 april */
1256 					/* what year should we use? */
1257 					year = (pref_time_year < current_time_year) ? current_time_year : pref_time_year;
1258 					/* calculate time of specified weekday of specific month */
1259 					start_time = calculate_time_from_weekday_of_month(year, temp_daterange->smon, temp_daterange->swday, temp_daterange->swday_offset);
1260 					/* advance to next year if we've passed this month weekday already this year */
1261 					if(start_time < preferred_time) {
1262 						year++;
1263 						start_time = calculate_time_from_weekday_of_month(year, temp_daterange->smon, temp_daterange->swday, temp_daterange->swday_offset);
1264 						}
1265 					break;
1266 				case DATERANGE_WEEK_DAY: /* wednesday 1 */
1267 					/* what year should we use? */
1268 					year = (pref_time_year < current_time_year) ? current_time_year : pref_time_year;
1269 					/* calculate time of specified weekday of month */
1270 					start_time = calculate_time_from_weekday_of_month(year, pref_time_mon, temp_daterange->swday, temp_daterange->swday_offset);
1271 					/* advance to next month (or year) if we've passed this weekday of this month already */
1272 					if(start_time < preferred_time) {
1273 						month = pref_time_mon;
1274 						if(month == 11) {
1275 							month = 0;
1276 							year++;
1277 							}
1278 						else
1279 							month++;
1280 						start_time = calculate_time_from_weekday_of_month(year, month, temp_daterange->swday, temp_daterange->swday_offset);
1281 						}
1282 					break;
1283 				default:
1284 					continue;
1285 					break;
1286 				}
1287 
1288 #ifdef TEST_TIMEPERIODS_B
1289 			printf("START TIME: %lu = %s", start_time, ctime(&start_time));
1290 #endif
1291 
1292 			/* get the end time */
1293 			switch(daterange_type) {
1294 				case DATERANGE_CALENDAR_DATE:
1295 					t->tm_sec = 0;
1296 					t->tm_min = 0;
1297 					t->tm_hour = 0;
1298 					t->tm_mday = temp_daterange->emday;
1299 					t->tm_mon = temp_daterange->emon;
1300 					t->tm_year = (temp_daterange->eyear - 1900);
1301 					t->tm_isdst = -1;
1302 					end_time = mktime(t);
1303 					break;
1304 				case DATERANGE_MONTH_DATE:
1305 					/* use same year as was calculated for start time above */
1306 					end_time = calculate_time_from_day_of_month(year, temp_daterange->emon, temp_daterange->emday);
1307 					/* advance a year if necessary: august 5 - feburary 2 */
1308 					if(end_time < start_time) {
1309 						year++;
1310 						end_time = calculate_time_from_day_of_month(year, temp_daterange->emon, temp_daterange->emday);
1311 						}
1312 					break;
1313 				case DATERANGE_MONTH_DAY:
1314 					/* use same year and month as was calculated for start time above */
1315 					end_time = calculate_time_from_day_of_month(year, month, temp_daterange->emday);
1316 					break;
1317 				case DATERANGE_MONTH_WEEK_DAY:
1318 					/* use same year as was calculated for start time above */
1319 					end_time = calculate_time_from_weekday_of_month(year, temp_daterange->emon, temp_daterange->ewday, temp_daterange->ewday_offset);
1320 					/* advance a year if necessary: thursday 2 august - monday 3 february */
1321 					if(end_time < start_time) {
1322 						year++;
1323 						end_time = calculate_time_from_weekday_of_month(year, temp_daterange->emon, temp_daterange->ewday, temp_daterange->ewday_offset);
1324 						}
1325 					break;
1326 				case DATERANGE_WEEK_DAY:
1327 					/* use same year and month as was calculated for start time above */
1328 					end_time = calculate_time_from_weekday_of_month(year, month, temp_daterange->ewday, temp_daterange->ewday_offset);
1329 					break;
1330 				default:
1331 					continue;
1332 					break;
1333 				}
1334 
1335 #ifdef TEST_TIMEPERIODS_B
1336 			printf("STARTTIME: %lu = %s", (unsigned long)start_time, ctime(&start_time));
1337 			printf("ENDTIME1: %lu = %s", (unsigned long)end_time, ctime(&end_time));
1338 #endif
1339 
1340 			/* start date was bad, so skip this date range */
1341 			if((unsigned long)start_time == 0L)
1342 				continue;
1343 
1344 			/* end date was bad - see if we can handle the error */
1345 			if((unsigned long)end_time == 0L) {
1346 				switch(daterange_type) {
1347 					case DATERANGE_CALENDAR_DATE:
1348 						continue;
1349 						break;
1350 					case DATERANGE_MONTH_DATE:
1351 						/* end date can't be helped, so skip it */
1352 						if(temp_daterange->emday < 0)
1353 							continue;
1354 
1355 						/* else end date slipped past end of month, so use last day of month as end date */
1356 						end_time = calculate_time_from_day_of_month(year, temp_daterange->emon, -1);
1357 						break;
1358 					case DATERANGE_MONTH_DAY:
1359 						/* end date can't be helped, so skip it */
1360 						if(temp_daterange->emday < 0)
1361 							continue;
1362 
1363 						/* else end date slipped past end of month, so use last day of month as end date */
1364 						end_time = calculate_time_from_day_of_month(year, month, -1);
1365 						break;
1366 					case DATERANGE_MONTH_WEEK_DAY:
1367 						/* end date can't be helped, so skip it */
1368 						if(temp_daterange->ewday_offset < 0)
1369 							continue;
1370 
1371 						/* else end date slipped past end of month, so use last day of month as end date */
1372 						end_time = calculate_time_from_day_of_month(year, pref_time_mon, -1);
1373 						break;
1374 					case DATERANGE_WEEK_DAY:
1375 						/* end date can't be helped, so skip it */
1376 						if(temp_daterange->ewday_offset < 0)
1377 							continue;
1378 
1379 						/* else end date slipped past end of month, so use last day of month as end date */
1380 						end_time = calculate_time_from_day_of_month(year, month, -1);
1381 						break;
1382 					default:
1383 						continue;
1384 						break;
1385 					}
1386 				}
1387 
1388 #ifdef TEST_TIMEPERIODS_B
1389 			printf("ENDTIME2: %lu = %s", (unsigned long)end_time, ctime(&end_time));
1390 #endif
1391 
1392 			/* if skipping days... */
1393 			if(temp_daterange->skip_interval > 1) {
1394 
1395 				/* advance to the next possible skip date */
1396 				if(start_time < preferred_time) {
1397 					/* check if interval is across dlst change and gets the compensation */
1398 					shift = get_dst_shift(&start_time, &midnight);
1399 
1400 					/* how many days have passed between skip start date and preferred time? */
1401 					days = (shift + (unsigned long)midnight - (unsigned long)start_time) / (3600 * 24);
1402 
1403 #ifdef TEST_TIMEPERIODS_B
1404 					printf("MIDNIGHT: %lu = %s", midnight, ctime(&midnight));
1405 					printf("%lu SECONDS PASSED\n", (midnight - (unsigned long)start_time));
1406 					printf("%d DAYS PASSED\n", days);
1407 					printf("REMAINDER: %d\n", (days % temp_daterange->skip_interval));
1408 					printf("SKIP INTERVAL: %d\n", temp_daterange->skip_interval);
1409 					printf("DLST SHIFT: %d", shift);
1410 #endif
1411 
1412 					/* advance start date to next skip day */
1413 					if((days % temp_daterange->skip_interval) == 0)
1414 						start_time += (days * 3600 * 24);
1415 					else
1416 						start_time += ((days - (days % temp_daterange->skip_interval) + temp_daterange->skip_interval) * 3600 * 24);
1417 					}
1418 
1419 				/* if skipping has no end, use start date as end */
1420 				if((daterange_type == DATERANGE_CALENDAR_DATE) && is_daterange_single_day(temp_daterange) == TRUE)
1421 					end_time = start_time;
1422 				}
1423 
1424 #ifdef TEST_TIMEPERIODS_B
1425 			printf("\nSTART:     %lu = %s", (unsigned long)start_time, ctime(&start_time));
1426 			printf("END:       %lu = %s", (unsigned long)end_time, ctime(&end_time));
1427 			printf("PREFERRED: %lu = %s", (unsigned long)preferred_time, ctime(&preferred_time));
1428 			printf("CURRENT:   %lu = %s", (unsigned long)current_time, ctime(&current_time));
1429 #endif
1430 
1431 			/* skip this date range its out of bounds with what we want */
1432 			if(preferred_time > end_time)
1433 				continue;
1434 
1435 			/* how many days at a time should we advance? */
1436 			if(temp_daterange->skip_interval > 1)
1437 				advance_interval = temp_daterange->skip_interval;
1438 			else
1439 				advance_interval = 1;
1440 
1441 			/* advance through the date range */
1442 			for(day_start = start_time; day_start <= end_time; day_start += (advance_interval * 3600 * 24)) {
1443 
1444 				/* we already found a time from a higher-precendence date range exception */
1445 				if(day_start >= earliest_day && have_earliest_time == TRUE)
1446 					continue;
1447 
1448 				for(temp_timerange = temp_daterange->times; temp_timerange != NULL; temp_timerange = temp_timerange->next) {
1449 
1450 					/* ranges with start/end of zero mean exlude this day */
1451 					if(temp_timerange->range_start == 0 && temp_timerange->range_end == 0)
1452 						continue;
1453 
1454 					day_range_start = (time_t)(day_start + temp_timerange->range_start);
1455 					day_range_end = (time_t)(day_start + temp_timerange->range_end);
1456 
1457 #ifdef TEST_TIMEPERIODS_B
1458 					printf("  RANGE START: %lu (%lu) = %s", temp_timerange->range_start, (unsigned long)day_range_start, ctime(&day_range_start));
1459 					printf("  RANGE END:   %lu (%lu) = %s", temp_timerange->range_end, (unsigned long)day_range_end, ctime(&day_range_end));
1460 #endif
1461 
1462 					/* range is out of bounds */
1463 					if(day_range_end < preferred_time)
1464 						continue;
1465 
1466 					/* preferred time occurs before range start, so use range start time as earliest potential time */
1467 					if(day_range_start >= preferred_time)
1468 						potential_time = day_range_start;
1469 					/* preferred time occurs between range start/end, so use preferred time as earliest potential time */
1470 					else if(day_range_end >= preferred_time)
1471 						potential_time = preferred_time;
1472 
1473 					/* is this the earliest time found thus far? */
1474 					if(have_earliest_time == FALSE || potential_time < earliest_time) {
1475 						have_earliest_time = TRUE;
1476 						earliest_time = potential_time;
1477 						earliest_day = day_start;
1478 #ifdef TEST_TIMEPERIODS_B
1479 						printf("    EARLIEST TIME: %lu = %s", (unsigned long)earliest_time, ctime(&earliest_time));
1480 #endif
1481 						}
1482 					}
1483 				}
1484 			}
1485 
1486 		}
1487 
1488 
1489 	/**** find next available time from normal, weekly rotating schedule (in this timeperiod definition) ****/
1490 
1491 	/* check a one week rotation of time */
1492 	has_looped = FALSE;
1493 	for(weekday = pref_time_wday, days_into_the_future = 0;; weekday++, days_into_the_future++) {
1494 
1495 		/* break out of the loop if we have checked an entire week already */
1496 		if(has_looped == TRUE && weekday >= pref_time_wday)
1497 			break;
1498 
1499 		if(weekday >= 7) {
1500 			weekday -= 7;
1501 			has_looped = TRUE;
1502 			}
1503 
1504 		/* calculate start of this future weekday */
1505 		day_start = (time_t)(midnight + (days_into_the_future * 3600 * 24));
1506 
1507 		/* we already found a time from a higher-precendence date range exception */
1508 		if(day_start == earliest_day)
1509 			continue;
1510 
1511 		/* check all time ranges for this day of the week */
1512 		for(temp_timerange = tperiod->days[weekday]; temp_timerange != NULL; temp_timerange = temp_timerange->next) {
1513 
1514 			/* calculate the time for the start of this time range */
1515 			day_range_start = (time_t)(day_start + temp_timerange->range_start);
1516 
1517 			if((have_earliest_time == FALSE || day_range_start < earliest_time) && day_range_start >= preferred_time) {
1518 				have_earliest_time = TRUE;
1519 				earliest_time = day_range_start;
1520 				earliest_day = day_start;
1521 				}
1522 			}
1523 		}
1524 
1525 
1526 	/* if we couldn't find a time period there must be none defined */
1527 	if(have_earliest_time == FALSE || earliest_time == (time_t)0)
1528 		*valid_time = (time_t)preferred_time;
1529 
1530 	/* else use the calculated time */
1531 	else
1532 		*valid_time = earliest_time;
1533 
1534 	return;
1535 	}
1536 
1537 
1538 /* given a preferred time, get the next valid time within a time period */
get_next_valid_time(time_t pref_time,time_t * valid_time,timeperiod * tperiod)1539 void get_next_valid_time(time_t pref_time, time_t *valid_time, timeperiod *tperiod) {
1540 	time_t current_time = (time_t)0L;
1541 
1542 	log_debug_info(DEBUGL_FUNCTIONS, 0, "get_next_valid_time()\n");
1543 
1544 	/* get time right now, preferred time must be now or in the future */
1545 	time(&current_time);
1546 
1547 	_get_next_valid_time(pref_time, current_time, valid_time, tperiod);
1548 	}
1549 
1550 
1551 /* tests if a date range covers just a single day */
is_daterange_single_day(daterange * dr)1552 int is_daterange_single_day(daterange *dr) {
1553 
1554 	if(dr == NULL)
1555 		return FALSE;
1556 
1557 	if(dr->syear != dr->eyear)
1558 		return FALSE;
1559 	if(dr->smon != dr->emon)
1560 		return FALSE;
1561 	if(dr->smday != dr->emday)
1562 		return FALSE;
1563 	if(dr->swday != dr->ewday)
1564 		return FALSE;
1565 	if(dr->swday_offset != dr->ewday_offset)
1566 		return FALSE;
1567 
1568 	return TRUE;
1569 	}
1570 
1571 
1572 
1573 /* returns a time (midnight) of particular (3rd, last) day in a given month */
calculate_time_from_day_of_month(int year,int month,int monthday)1574 time_t calculate_time_from_day_of_month(int year, int month, int monthday) {
1575 	time_t midnight;
1576 	int day = 0;
1577 	struct tm t;
1578 
1579 #ifdef TEST_TIMEPERIODS
1580 	printf("YEAR: %d, MON: %d, MDAY: %d\n", year, month, monthday);
1581 #endif
1582 
1583 	/* positive day (3rd day) */
1584 	if(monthday > 0) {
1585 
1586 		t.tm_sec = 0;
1587 		t.tm_min = 0;
1588 		t.tm_hour = 0;
1589 		t.tm_year = year;
1590 		t.tm_mon = month;
1591 		t.tm_mday = monthday;
1592 		t.tm_isdst = -1;
1593 
1594 		midnight = mktime(&t);
1595 
1596 #ifdef TEST_TIMEPERIODS
1597 		printf("MIDNIGHT CALC: %s", ctime(&midnight));
1598 #endif
1599 
1600 		/* if we rolled over to the next month, time is invalid */
1601 		/* assume the user's intention is to keep it in the current month */
1602 		if(t.tm_mon != month)
1603 			midnight = (time_t)0L;
1604 		}
1605 
1606 	/* negative offset (last day, 3rd to last day) */
1607 	else {
1608 		/* find last day in the month */
1609 		day = 32;
1610 		do {
1611 			/* back up a day */
1612 			day--;
1613 
1614 			/* make the new time */
1615 			t.tm_mon = month;
1616 			t.tm_year = year;
1617 			t.tm_mday = day;
1618 			t.tm_isdst = -1;
1619 			midnight = mktime(&t);
1620 
1621 			}
1622 		while(t.tm_mon != month);
1623 
1624 		/* now that we know the last day, back up more */
1625 		/* make the new time */
1626 		t.tm_mon = month;
1627 		t.tm_year = year;
1628 		/* -1 means last day of month, so add one to to make this correct - Mike Bird */
1629 		t.tm_mday += (monthday < -30) ? -30 : monthday + 1;
1630 		t.tm_isdst = -1;
1631 		midnight = mktime(&t);
1632 
1633 		/* if we rolled over to the previous month, time is invalid */
1634 		/* assume the user's intention is to keep it in the current month */
1635 		if(t.tm_mon != month)
1636 			midnight = (time_t)0L;
1637 		}
1638 
1639 	return midnight;
1640 	}
1641 
1642 
1643 
1644 /* returns a time (midnight) of particular (3rd, last) weekday in a given month */
calculate_time_from_weekday_of_month(int year,int month,int weekday,int weekday_offset)1645 time_t calculate_time_from_weekday_of_month(int year, int month, int weekday, int weekday_offset) {
1646 	time_t midnight;
1647 	int days = 0;
1648 	int weeks = 0;
1649 	struct tm t;
1650 
1651 	t.tm_sec = 0;
1652 	t.tm_min = 0;
1653 	t.tm_hour = 0;
1654 	t.tm_year = year;
1655 	t.tm_mon = month;
1656 	t.tm_mday = 1;
1657 	t.tm_isdst = -1;
1658 
1659 	midnight = mktime(&t);
1660 
1661 	/* how many days must we advance to reach the first instance of the weekday this month? */
1662 	days = weekday - (t.tm_wday);
1663 	if(days < 0)
1664 		days += 7;
1665 
1666 	/* positive offset (3rd thursday) */
1667 	if(weekday_offset > 0) {
1668 
1669 		/* how many weeks must we advance (no more than 5 possible) */
1670 		weeks = (weekday_offset > 5) ? 5 : weekday_offset;
1671 		days += ((weeks - 1) * 7);
1672 
1673 		/* make the new time */
1674 		t.tm_mon = month;
1675 		t.tm_year = year;
1676 		t.tm_mday = days + 1;
1677 		t.tm_isdst = -1;
1678 		midnight = mktime(&t);
1679 
1680 		/* if we rolled over to the next month, time is invalid */
1681 		/* assume the user's intention is to keep it in the current month */
1682 		if(t.tm_mon != month)
1683 			midnight = (time_t)0L;
1684 		}
1685 
1686 	/* negative offset (last thursday, 3rd to last tuesday) */
1687 	else {
1688 		/* find last instance of weekday in the month */
1689 		days += (5 * 7);
1690 		do {
1691 			/* back up a week */
1692 			days -= 7;
1693 
1694 			/* make the new time */
1695 			t.tm_mon = month;
1696 			t.tm_year = year;
1697 			t.tm_mday = days + 1;
1698 			t.tm_isdst = -1;
1699 			midnight = mktime(&t);
1700 
1701 			}
1702 		while(t.tm_mon != month);
1703 
1704 		/* now that we know the last instance of the weekday, back up more */
1705 		weeks = (weekday_offset < -5) ? -5 : weekday_offset;
1706 		days = ((weeks + 1) * 7);
1707 
1708 		/* make the new time */
1709 		t.tm_mon = month;
1710 		t.tm_year = year;
1711 		t.tm_mday += days;
1712 		t.tm_isdst = -1;
1713 		midnight = mktime(&t);
1714 
1715 		/* if we rolled over to the previous month, time is invalid */
1716 		/* assume the user's intention is to keep it in the current month */
1717 		if(t.tm_mon != month)
1718 			midnight = (time_t)0L;
1719 		}
1720 
1721 	return midnight;
1722 	}
1723 
1724 
1725 /* get the next time to schedule a log rotation */
get_next_log_rotation_time(void)1726 time_t get_next_log_rotation_time(void) {
1727 	time_t current_time;
1728 	struct tm *t, tm_s;
1729 	int is_dst_now = FALSE;
1730 	time_t run_time;
1731 
1732 	time(&current_time);
1733 	t = localtime_r(&current_time, &tm_s);
1734 	t->tm_min = 0;
1735 	t->tm_sec = 0;
1736 	is_dst_now = (t->tm_isdst > 0) ? TRUE : FALSE;
1737 
1738 	switch(log_rotation_method) {
1739 		case LOG_ROTATION_HOURLY:
1740 			t->tm_hour++;
1741 			run_time = mktime(t);
1742 			break;
1743 		case LOG_ROTATION_DAILY:
1744 			t->tm_mday++;
1745 			t->tm_hour = 0;
1746 			run_time = mktime(t);
1747 			break;
1748 		case LOG_ROTATION_WEEKLY:
1749 			t->tm_mday += (7 - t->tm_wday);
1750 			t->tm_hour = 0;
1751 			run_time = mktime(t);
1752 			break;
1753 		case LOG_ROTATION_MONTHLY:
1754 		default:
1755 			t->tm_mon++;
1756 			t->tm_mday = 1;
1757 			t->tm_hour = 0;
1758 			run_time = mktime(t);
1759 			break;
1760 		}
1761 
1762 	if(is_dst_now == TRUE && t->tm_isdst == 0)
1763 		run_time += 3600;
1764 	else if(is_dst_now == FALSE && t->tm_isdst > 0)
1765 		run_time -= 3600;
1766 
1767 	return run_time;
1768 	}
1769 
1770 
1771 
1772 /******************************************************************/
1773 /******************** SIGNAL HANDLER FUNCTIONS ********************/
1774 /******************************************************************/
1775 
1776 
1777 /* trap signals so we can exit gracefully */
setup_sighandler(void)1778 void setup_sighandler(void) {
1779 
1780 	/* reset the shutdown flag */
1781 	sigshutdown = FALSE;
1782 
1783 	/* remove buffering from stderr, stdin, and stdout */
1784 	setbuf(stdin, (char *)NULL);
1785 	setbuf(stdout, (char *)NULL);
1786 	setbuf(stderr, (char *)NULL);
1787 
1788 	/* initialize signal handling */
1789 	signal(SIGPIPE, SIG_IGN);
1790 	signal(SIGQUIT, sighandler);
1791 	signal(SIGTERM, sighandler);
1792 	signal(SIGHUP, sighandler);
1793 	if(daemon_dumps_core == FALSE && daemon_mode == TRUE)
1794 		signal(SIGSEGV, sighandler);
1795 
1796 	return;
1797 	}
1798 
1799 
1800 /* reset signal handling... */
reset_sighandler(void)1801 void reset_sighandler(void) {
1802 
1803 	/* set signal handling to default actions */
1804 	signal(SIGQUIT, SIG_DFL);
1805 	signal(SIGTERM, SIG_DFL);
1806 	signal(SIGHUP, SIG_DFL);
1807 	signal(SIGSEGV, SIG_DFL);
1808 	signal(SIGPIPE, SIG_DFL);
1809 	signal(SIGXFSZ, SIG_DFL);
1810 
1811 	return;
1812 	}
1813 
1814 
1815 /* handle signals */
sighandler(int sig)1816 void sighandler(int sig) {
1817 	int x = 0;
1818 
1819 	/* if shutdown is already true, we're in a signal trap loop! */
1820 	/* changed 09/07/06 to only exit on segfaults */
1821 	if(sigshutdown == TRUE && sig == SIGSEGV)
1822 		exit(ERROR);
1823 
1824 	caught_signal = TRUE;
1825 
1826 	if(sig < 0)
1827 		sig = -sig;
1828 
1829 	for(x = 0; sigs[x] != (char *)NULL; x++);
1830 	sig %= x;
1831 
1832 	sig_id = sig;
1833 
1834 	/* log errors about segfaults now, as we might not get a chance to later */
1835 	/* all other signals are logged at a later point in main() to prevent problems with NPTL */
1836 	if(sig == SIGSEGV)
1837 		logit(NSLOG_PROCESS_INFO, TRUE, "Caught SIG%s, shutting down...\n", sigs[sig]);
1838 
1839 	/* we received a SIGHUP, so restart... */
1840 	if(sig == SIGHUP)
1841 		sigrestart = TRUE;
1842 
1843 	/* else begin shutting down... */
1844 	else if(sig < 16)
1845 		sigshutdown = TRUE;
1846 
1847 	return;
1848 	}
1849 
1850 /* Handle the SIGXFSZ signal. A SIGXFSZ signal is received when a file exceeds
1851 	the maximum allowable size either as dictated by the fzise paramater in
1852 	/etc/security/limits.conf (ulimit -f) or by the maximum size allowed by
1853 	the filesystem */
handle_sigxfsz(int sig)1854 void handle_sigxfsz(int sig) {
1855 
1856 	static time_t lastlog_time = (time_t)0;	/* Save the last log time so we
1857 											   don't log too often. */
1858 	unsigned long log_interval = 300;		/* How frequently to log messages
1859 											   about receiving the signal */
1860 	struct rlimit rlim;
1861 	time_t now;
1862 	char *files[] = {
1863 		log_file,
1864 		debug_file,
1865 		xpddefault_host_perfdata_file,
1866 		xpddefault_service_perfdata_file,
1867 		xodtemplate_cache_file,
1868 		xodtemplate_precache_file,
1869 		xsddefault_status_log,
1870 		xrddefault_retention_file,
1871 		};
1872 	int x;
1873 	char **filep;
1874 	long long size;
1875 	long long max_size = 0LL;
1876 	char *max_name = NULL;
1877 
1878 	if(SIGXFSZ == sig) {	/* Make sure we're handling the correct signal */
1879 		/* Check the current time and if less time has passed since the last
1880 			time the signal was received, ignore it */
1881 		time(&now);
1882 		if((unsigned long)(now - lastlog_time) < log_interval) return;
1883 
1884 		/* Get the current file size limit */
1885 		if(getrlimit(RLIMIT_FSIZE, &rlim) != 0) {
1886 			/* Attempt to log the error, realizing that the logging may fail
1887 				if it is the log file that is over the size limit. */
1888 			logit(NSLOG_RUNTIME_ERROR, TRUE,
1889 					"Unable to determine current resoure limits: %s\n",
1890 					strerror(errno));
1891 			}
1892 
1893 		/* Try to figure out which file caused the signal and react
1894 				appropriately */
1895 		for(x = 0, filep = files; x < (sizeof(files) / sizeof(files[0]));
1896 				x++, filep++) {
1897 			if((*filep != NULL) && strcmp(*filep, "/dev/null")) {
1898 				if((size = check_file_size(*filep, 1024, rlim)) == -1) {
1899 					lastlog_time = now;
1900 					return;
1901 					}
1902 				else if(size > max_size) {
1903 					max_size = size;
1904 					max_name = log_file;
1905 					}
1906 				}
1907 			}
1908 		/* TODO: Perhaps add check of the check results files in
1909 			check_results_path. This is likely not needed because these
1910 			files aren't very big */
1911 		if((max_size > 0) && (max_name != NULL)) {
1912 			logit(NSLOG_RUNTIME_ERROR, TRUE, "SIGXFSZ received because a "
1913 					"file's size may have exceeded the file size limits of "
1914 					"the filesystem. The largest file checked, '%s', has a "
1915 					"size of %lld bytes", max_name, max_size);
1916 
1917 			}
1918 		else {
1919 			logit(NSLOG_RUNTIME_ERROR, TRUE, "SIGXFSZ received but unable to "
1920 					"determine which file may have caused it.");
1921 			}
1922 		}
1923 	return;
1924 	}
1925 
1926 /* Checks a file to determine whether it exceeds resource limit imposed
1927 	limits. Returns the file size if file is OK, 0 if it's status could not
1928 	be determined, or -1 if not OK. fudge is the fudge factor (in bytes) for
1929 	checking the file size */
check_file_size(char * path,unsigned long fudge,struct rlimit rlim)1930 static long long check_file_size(char *path, unsigned long fudge,
1931 		struct rlimit rlim) {
1932 
1933 	struct stat status;
1934 
1935 	/* Make sure we were passed a legitimate file path */
1936 	if(NULL == path) {
1937 		return 0;
1938 		}
1939 
1940 	/* Get the status of the file */
1941 	if(stat(path, &status) == 0) {
1942 		/* Make sure it is a file */
1943 		if(S_ISREG(status.st_mode)) {
1944 			/* If the file size plus the fudge factor exceeds the
1945 				current resource limit imposed size limit, log an error */
1946 			if(status.st_size + fudge > rlim.rlim_cur) {
1947 				logit(NSLOG_RUNTIME_ERROR, TRUE, "Size of file '%s' (%llu) "
1948 						"exceeds (or nearly exceeds) size imposed by resource "
1949 						"limits (%llu). Consider increasing limits with "
1950 						"ulimit(1).\n", path,
1951 						(unsigned long long)status.st_size,
1952 						(unsigned long long)rlim.rlim_cur);
1953 				return -1;
1954 				}
1955 			else {
1956 				return status.st_size;
1957 				}
1958 			}
1959 		else {
1960 			return 0;
1961 			}
1962 		}
1963 	else {
1964 		/* If we could not determine the file status, log an error message */
1965 		logit(NSLOG_RUNTIME_ERROR, TRUE,
1966 				"Unable to determine status of file %s: %s\n",
1967 				log_file, strerror(errno));
1968 		return 0;
1969 		}
1970 	}
1971 
1972 /* handle timeouts when executing service checks */
1973 /* 07/16/08 EG also called when parent process gets a TERM signal */
service_check_sighandler(int sig)1974 void service_check_sighandler(int sig) {
1975 	struct timeval end_time;
1976 
1977 	/* get the current time */
1978 	gettimeofday(&end_time, NULL);
1979 
1980 	check_result_info.return_code = service_check_timeout_state;
1981 	check_result_info.finish_time = end_time;
1982 	check_result_info.early_timeout = TRUE;
1983 
1984 	/* write check result to file */
1985 	if(check_result_info.output_file_fp) {
1986 
1987 		fprintf(check_result_info.output_file_fp, "finish_time=%lu.%lu\n", check_result_info.finish_time.tv_sec, check_result_info.finish_time.tv_usec);
1988 		fprintf(check_result_info.output_file_fp, "early_timeout=%d\n", check_result_info.early_timeout);
1989 		fprintf(check_result_info.output_file_fp, "exited_ok=%d\n", check_result_info.exited_ok);
1990 		fprintf(check_result_info.output_file_fp, "return_code=%d\n", check_result_info.return_code);
1991 		fprintf(check_result_info.output_file_fp, "output=%s\n", "(Service Check Timed Out)");
1992 
1993 		/* close the temp file */
1994 		fclose(check_result_info.output_file_fp);
1995 
1996 		/* move check result to queue directory */
1997 		move_check_result_to_queue(check_result_info.output_file);
1998 		}
1999 
2000 	/* free check result memory */
2001 	free_check_result(&check_result_info);
2002 
2003 	/* try to kill the command that timed out by sending termination signal to our process group */
2004 	/* we also kill ourselves while doing this... */
2005 	kill((pid_t)0, SIGKILL);
2006 
2007 	/* force the child process (service check) to exit... */
2008 	_exit(STATE_CRITICAL);
2009 	}
2010 
2011 
2012 /* handle timeouts when executing host checks */
2013 /* 07/16/08 EG also called when parent process gets a TERM signal */
host_check_sighandler(int sig)2014 void host_check_sighandler(int sig) {
2015 	struct timeval end_time;
2016 
2017 	/* get the current time */
2018 	gettimeofday(&end_time, NULL);
2019 
2020 	check_result_info.return_code = STATE_CRITICAL;
2021 	check_result_info.finish_time = end_time;
2022 	check_result_info.early_timeout = TRUE;
2023 
2024 	/* write check result to file */
2025 	if(check_result_info.output_file_fp) {
2026 
2027 		fprintf(check_result_info.output_file_fp, "finish_time=%lu.%lu\n", check_result_info.finish_time.tv_sec, check_result_info.finish_time.tv_usec);
2028 		fprintf(check_result_info.output_file_fp, "early_timeout=%d\n", check_result_info.early_timeout);
2029 		fprintf(check_result_info.output_file_fp, "exited_ok=%d\n", check_result_info.exited_ok);
2030 		fprintf(check_result_info.output_file_fp, "return_code=%d\n", check_result_info.return_code);
2031 		fprintf(check_result_info.output_file_fp, "output=%s\n", "(Host Check Timed Out)");
2032 
2033 		/* close the temp file */
2034 		fclose(check_result_info.output_file_fp);
2035 
2036 		/* move check result to queue directory */
2037 		move_check_result_to_queue(check_result_info.output_file);
2038 		}
2039 
2040 	/* free check result memory */
2041 	free_check_result(&check_result_info);
2042 
2043 	/* try to kill the command that timed out by sending termination signal to our process group */
2044 	/* we also kill ourselves while doing this... */
2045 	kill((pid_t)0, SIGKILL);
2046 
2047 	/* force the child process (service check) to exit... */
2048 	_exit(STATE_CRITICAL);
2049 	}
2050 
2051 
2052 /* handle timeouts when executing commands via my_system_r() */
my_system_sighandler(int sig)2053 void my_system_sighandler(int sig) {
2054 
2055 	/* force the child process to exit... */
2056 	_exit(STATE_CRITICAL);
2057 	}
2058 
2059 
2060 
2061 
2062 /******************************************************************/
2063 /************************ DAEMON FUNCTIONS ************************/
2064 /******************************************************************/
2065 
daemon_init(void)2066 int daemon_init(void) {
2067 	pid_t pid = -1;
2068 	int pidno = 0;
2069 	int lockfile = 0;
2070 	int val = 0;
2071 	char buf[256];
2072 	struct flock lock;
2073 	char *homedir = NULL;
2074 
2075 #ifdef RLIMIT_CORE
2076 	struct rlimit limit;
2077 #endif
2078 
2079 	/* change working directory. scuttle home if we're dumping core */
2080 	homedir = getenv("HOME");
2081 	if(daemon_dumps_core == TRUE && homedir != NULL)
2082 		chdir(homedir);
2083 	else
2084 		chdir("/");
2085 
2086 	umask(S_IWGRP | S_IWOTH);
2087 
2088 	lockfile = open(lock_file, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
2089 
2090 	if(lockfile < 0) {
2091 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Failed to obtain lock on file %s: %s\n", lock_file, strerror(errno));
2092 		logit(NSLOG_PROCESS_INFO | NSLOG_RUNTIME_ERROR, TRUE, "Bailing out due to errors encountered while attempting to daemonize... (PID=%d)", (int)getpid());
2093 
2094 		cleanup();
2095 		exit(ERROR);
2096 		}
2097 
2098 	/* see if we can read the contents of the lockfile */
2099 	if((val = read(lockfile, buf, (size_t)10)) < 0) {
2100 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Lockfile exists but cannot be read");
2101 		cleanup();
2102 		exit(ERROR);
2103 		}
2104 
2105 	/* we read something - check the PID */
2106 	if(val > 0) {
2107 		if((val = sscanf(buf, "%d", &pidno)) < 1) {
2108 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Lockfile '%s' does not contain a valid PID (%s)", lock_file, buf);
2109 			cleanup();
2110 			exit(ERROR);
2111 			}
2112 		}
2113 
2114 	/* check for SIGHUP */
2115 	if(val == 1 && (pid = (pid_t)pidno) == getpid()) {
2116 		close(lockfile);
2117 		return OK;
2118 		}
2119 
2120 	/* exit on errors... */
2121 	if((pid = fork()) < 0)
2122 		return(ERROR);
2123 
2124 	/* parent process goes away.. */
2125 	else if((int)pid != 0)
2126 		exit(OK);
2127 
2128 	/* child continues... */
2129 
2130 	/* child becomes session leader... */
2131 	setsid();
2132 
2133 	/* place a file lock on the lock file */
2134 	lock.l_type = F_WRLCK;
2135 	lock.l_start = 0;
2136 	lock.l_whence = SEEK_SET;
2137 	lock.l_len = 0;
2138 	if(fcntl(lockfile, F_SETLK, &lock) < 0) {
2139 		if(errno == EACCES || errno == EAGAIN) {
2140 			fcntl(lockfile, F_GETLK, &lock);
2141 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Lockfile '%s' looks like its already held by another instance of Nagios (PID %d).  Bailing out...", lock_file, (int)lock.l_pid);
2142 			}
2143 		else
2144 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Cannot lock lockfile '%s': %s. Bailing out...", lock_file, strerror(errno));
2145 
2146 		cleanup();
2147 		exit(ERROR);
2148 		}
2149 
2150 	/* prevent daemon from dumping a core file... */
2151 #ifdef RLIMIT_CORE
2152 	if(daemon_dumps_core == FALSE) {
2153 		getrlimit(RLIMIT_CORE, &limit);
2154 		limit.rlim_cur = 0;
2155 		setrlimit(RLIMIT_CORE, &limit);
2156 		}
2157 #endif
2158 
2159 	/* write PID to lockfile... */
2160 	lseek(lockfile, 0, SEEK_SET);
2161 	ftruncate(lockfile, 0);
2162 	sprintf(buf, "%d\n", (int)getpid());
2163 	write(lockfile, buf, strlen(buf));
2164 
2165 	/* make sure lock file stays open while program is executing... */
2166 	val = fcntl(lockfile, F_GETFD, 0);
2167 	val |= FD_CLOEXEC;
2168 	fcntl(lockfile, F_SETFD, val);
2169 
2170 	/* close existing stdin, stdout, stderr */
2171 	close(0);
2172 	close(1);
2173 	close(2);
2174 
2175 	/* THIS HAS TO BE DONE TO AVOID PROBLEMS WITH STDERR BEING REDIRECTED TO SERVICE MESSAGE PIPE! */
2176 	/* re-open stdin, stdout, stderr with known values */
2177 	open("/dev/null", O_RDONLY);
2178 	open("/dev/null", O_WRONLY);
2179 	open("/dev/null", O_WRONLY);
2180 
2181 #ifdef USE_EVENT_BROKER
2182 	/* send program data to broker */
2183 	broker_program_state(NEBTYPE_PROCESS_DAEMONIZE, NEBFLAG_NONE, NEBATTR_NONE, NULL);
2184 #endif
2185 
2186 	return OK;
2187 	}
2188 
2189 
2190 
2191 /******************************************************************/
2192 /*********************** SECURITY FUNCTIONS ***********************/
2193 /******************************************************************/
2194 
2195 /* drops privileges */
drop_privileges(char * user,char * group)2196 int drop_privileges(char *user, char *group) {
2197 	uid_t uid = -1;
2198 	gid_t gid = -1;
2199 	struct group *grp = NULL;
2200 	struct passwd *pw = NULL;
2201 	int result = OK;
2202 
2203 	log_debug_info(DEBUGL_FUNCTIONS, 0, "drop_privileges() start\n");
2204 	log_debug_info(DEBUGL_PROCESS, 0, "Original UID/GID: %d/%d\n", (int)getuid(), (int)getgid());
2205 
2206 	/* only drop privileges if we're running as root, so we don't interfere with being debugged while running as some random user */
2207 	if(getuid() != 0)
2208 		return OK;
2209 
2210 	/* set effective group ID */
2211 	if(group != NULL) {
2212 
2213 		/* see if this is a group name */
2214 		if(strspn(group, "0123456789") < strlen(group)) {
2215 			grp = (struct group *)getgrnam(group);
2216 			if(grp != NULL)
2217 				gid = (gid_t)(grp->gr_gid);
2218 			else
2219 				logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Could not get group entry for '%s'", group);
2220 			}
2221 
2222 		/* else we were passed the GID */
2223 		else
2224 			gid = (gid_t)atoi(group);
2225 
2226 		/* set effective group ID if other than current EGID */
2227 		if(gid != getegid()) {
2228 
2229 			if(setgid(gid) == -1) {
2230 				logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Could not set effective GID=%d", (int)gid);
2231 				result = ERROR;
2232 				}
2233 			}
2234 		}
2235 
2236 
2237 	/* set effective user ID */
2238 	if(user != NULL) {
2239 
2240 		/* see if this is a user name */
2241 		if(strspn(user, "0123456789") < strlen(user)) {
2242 			pw = (struct passwd *)getpwnam(user);
2243 			if(pw != NULL)
2244 				uid = (uid_t)(pw->pw_uid);
2245 			else
2246 				logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Could not get passwd entry for '%s'", user);
2247 			}
2248 
2249 		/* else we were passed the UID */
2250 		else
2251 			uid = (uid_t)atoi(user);
2252 
2253 #ifdef HAVE_INITGROUPS
2254 
2255 		if(uid != geteuid()) {
2256 
2257 			/* initialize supplementary groups */
2258 			if(initgroups(user, gid) == -1) {
2259 				if(errno == EPERM)
2260 					logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Unable to change supplementary groups using initgroups() -- I hope you know what you're doing");
2261 				else {
2262 					logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Possibly root user failed dropping privileges with initgroups()");
2263 					return ERROR;
2264 					}
2265 				}
2266 			}
2267 #endif
2268 		/* Change the ownership on the debug log file */
2269 		chown_debug_log(uid, gid);
2270 
2271 		if(setuid(uid) == -1) {
2272 			logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Could not set effective UID=%d", (int)uid);
2273 			result = ERROR;
2274 			}
2275 		}
2276 
2277 	log_debug_info(DEBUGL_PROCESS, 0, "New UID/GID: %d/%d\n", (int)getuid(), (int)getgid());
2278 
2279 	return result;
2280 	}
2281 
2282 
2283 
2284 
2285 /******************************************************************/
2286 /************************* IPC FUNCTIONS **************************/
2287 /******************************************************************/
2288 
2289 /* move check result to queue directory */
move_check_result_to_queue(char * checkresult_file)2290 int move_check_result_to_queue(char *checkresult_file) {
2291 	char *output_file = NULL;
2292 	char *temp_buffer = NULL;
2293 	int output_file_fd = -1;
2294 	mode_t new_umask = 077;
2295 	mode_t old_umask;
2296 	int result = 0;
2297 
2298 	/* save the file creation mask */
2299 	old_umask = umask(new_umask);
2300 
2301 	/* create a safe temp file */
2302 	asprintf(&output_file, "%s/cXXXXXX", check_result_path);
2303 	output_file_fd = mkstemp(output_file);
2304 
2305 	/* file created okay */
2306 	if(output_file_fd >= 0) {
2307 
2308 		log_debug_info(DEBUGL_CHECKS, 2, "Moving temp check result file '%s' to queue file '%s'...\n", checkresult_file, output_file);
2309 
2310 #ifdef __CYGWIN__
2311 		/* Cygwin cannot rename open files - gives Permission Denied */
2312 		/* close the file */
2313 		close(output_file_fd);
2314 #endif
2315 
2316 		/* move the original file */
2317 		result = my_rename(checkresult_file, output_file);
2318 
2319 #ifndef __CYGWIN__
2320 		/* close the file */
2321 		close(output_file_fd);
2322 #endif
2323 
2324 		/* create an ok-to-go indicator file */
2325 		asprintf(&temp_buffer, "%s.ok", output_file);
2326 		if((output_file_fd = open(temp_buffer, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) >= 0)
2327 			close(output_file_fd);
2328 		my_free(temp_buffer);
2329 
2330 		/* delete the original file if it couldn't be moved */
2331 		if(result != 0)
2332 			unlink(checkresult_file);
2333 		}
2334 	else
2335 		result = -1;
2336 
2337 	/* reset the file creation mask */
2338 	umask(old_umask);
2339 
2340 	/* log a warning on errors */
2341 	if(result != 0)
2342 		logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Unable to move file '%s' to check results queue.\n", checkresult_file);
2343 
2344 	/* free memory */
2345 	my_free(output_file);
2346 
2347 	return OK;
2348 	}
2349 
2350 
2351 
2352 /* processes files in the check result queue directory */
process_check_result_queue(char * dirname)2353 int process_check_result_queue(char *dirname) {
2354 	char file[MAX_FILENAME_LENGTH];
2355 	DIR *dirp = NULL;
2356 	struct dirent *dirfile = NULL;
2357 	register int x = 0;
2358 	struct stat stat_buf;
2359 	struct stat ok_stat_buf;
2360 	char *temp_buffer = NULL;
2361 	int result = OK;
2362 
2363 	/* make sure we have what we need */
2364 	if(dirname == NULL) {
2365 		logit(NSLOG_CONFIG_ERROR, TRUE, "Error: No check result queue directory specified.\n");
2366 		return ERROR;
2367 		}
2368 
2369 	/* open the directory for reading */
2370 	if((dirp = opendir(dirname)) == NULL) {
2371 		logit(NSLOG_CONFIG_ERROR, TRUE, "Error: Could not open check result queue directory '%s' for reading.\n", dirname);
2372 		return ERROR;
2373 		}
2374 
2375 	log_debug_info(DEBUGL_CHECKS, 1, "Starting to read check result queue '%s'...\n", dirname);
2376 
2377 	/* process all files in the directory... */
2378 	while((dirfile = readdir(dirp)) != NULL) {
2379 
2380 		/* create /path/to/file */
2381 		snprintf(file, sizeof(file), "%s/%s", dirname, dirfile->d_name);
2382 		file[sizeof(file) - 1] = '\x0';
2383 
2384 		/* process this if it's a check result file... */
2385 		x = strlen(dirfile->d_name);
2386 		if(x == 7 && dirfile->d_name[0] == 'c') {
2387 
2388 			if(stat(file, &stat_buf) == -1) {
2389 				logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Could not stat() check result file '%s'.\n", file);
2390 				continue;
2391 				}
2392 
2393 			switch(stat_buf.st_mode & S_IFMT) {
2394 
2395 				case S_IFREG:
2396 					/* don't process symlinked files */
2397 					if(!S_ISREG(stat_buf.st_mode))
2398 						continue;
2399 					break;
2400 
2401 				default:
2402 					/* everything else we ignore */
2403 					continue;
2404 					break;
2405 				}
2406 
2407 			/* at this point we have a regular file... */
2408 
2409 			/* can we find the associated ok-to-go file ? */
2410 			asprintf(&temp_buffer, "%s.ok", file);
2411 			result = stat(temp_buffer, &ok_stat_buf);
2412 			my_free(temp_buffer);
2413 			if(result == -1)
2414 				continue;
2415 
2416 			/* process the file */
2417 			result = process_check_result_file(file, &check_result_list,
2418 			                                   TRUE, TRUE);
2419 
2420 			/* break out if we encountered an error */
2421 			if(result == ERROR)
2422 				break;
2423 			}
2424 		}
2425 
2426 	closedir(dirp);
2427 
2428 	return result;
2429 
2430 	}
2431 
2432 
2433 
2434 
2435 /* Find checks that are currently executing. This function is intended to
2436    be used on a Nagios restart to prevent currently executing checks from
2437    being rescheduled. */
find_executing_checks(char * dirname)2438 int find_executing_checks(char *dirname) {
2439 	char file[MAX_FILENAME_LENGTH];
2440 	DIR *dirp = NULL;
2441 	struct dirent *dirfile = NULL;
2442 	int x = 0;
2443 	struct stat stat_buf;
2444 	int result = OK;
2445 	check_result *crl = NULL;
2446 	check_result *cr = NULL;
2447 	service *svc = NULL;
2448 	host *host = NULL;
2449 	time_t current_time;
2450 
2451 	log_debug_info(DEBUGL_FUNCTIONS, 0, "find_executing_checks() start\n");
2452 
2453 	/* make sure we have what we need */
2454 	if(dirname == NULL) {
2455 		logit(NSLOG_CONFIG_ERROR, TRUE, "Error: No check directory specified.\n");
2456 		return ERROR;
2457 		}
2458 
2459 	/* open the directory for reading */
2460 	if((dirp = opendir(dirname)) == NULL) {
2461 		logit(NSLOG_CONFIG_ERROR, TRUE, "Error: Could not open check directory '%s' for reading.\n", dirname);
2462 		return ERROR;
2463 		}
2464 
2465 	log_debug_info(DEBUGL_CHECKS, 1, "Starting to read check directory '%s'...\n", dirname);
2466 
2467 	/* process all files in the directory... */
2468 	while((dirfile = readdir(dirp)) != NULL) {
2469 
2470 		/* create /path/to/file */
2471 		snprintf(file, sizeof(file), "%s/%s", dirname, dirfile->d_name);
2472 		file[sizeof(file) - 1] = '\x0';
2473 
2474 		/* process this if it's a check result file... */
2475 		x = strlen(dirfile->d_name);
2476 		if(x == 11 && !strncmp(dirfile->d_name, "check", 5)) {
2477 
2478 			if(stat(file, &stat_buf) == -1) {
2479 				logit(NSLOG_RUNTIME_WARNING, TRUE, "Warning: Could not stat() check status '%s'.\n", file);
2480 				continue;
2481 				}
2482 
2483 			switch(stat_buf.st_mode & S_IFMT) {
2484 
2485 				case S_IFREG:
2486 					/* don't process symlinked files */
2487 					if(!S_ISREG(stat_buf.st_mode))
2488 						continue;
2489 					break;
2490 
2491 				default:
2492 					/* everything else we ignore */
2493 					continue;
2494 					break;
2495 				}
2496 
2497 			/* at this point we have a regular file... */
2498 			log_debug_info(DEBUGL_CHECKS, 2,
2499 			               "Looking for still-executing checks in %s.\n", file);
2500 
2501 			/* process the file */
2502 			result = process_check_result_file(file, &crl, FALSE, FALSE);
2503 
2504 			/* break out if we encountered an error */
2505 			if(result == ERROR)
2506 				break;
2507 
2508 			time(&current_time);
2509 
2510 			/* examine the check results */
2511 			while((cr = read_check_result(&crl)) != NULL) {
2512 				if(HOST_CHECK == cr->object_check_type) {
2513 					log_debug_info(DEBUGL_CHECKS, 2,
2514 					               "Determining whether check for host '%s' is still executing.\n",
2515 					               cr->host_name);
2516 					if((host = find_host(cr->host_name)) == NULL) {
2517 						logit(NSLOG_RUNTIME_WARNING, TRUE,
2518 						      "Warning: Check status contained host '%s', "
2519 						      "but the host could not be found! Ignoring "
2520 						      "check.\n", cr->host_name);
2521 						}
2522 					else if(current_time - cr->start_time.tv_sec <
2523 					        host_check_timeout) {
2524 						log_debug_info(DEBUGL_CHECKS, 1,
2525 						               "Check for host %s is still executing.\n",
2526 						               cr->host_name);
2527 						host->is_executing = TRUE;
2528 						}
2529 					}
2530 				else if(SERVICE_CHECK == cr->object_check_type) {
2531 					log_debug_info(DEBUGL_CHECKS, 2,
2532 					               "Determining whether check for service '%s' on host '%s' is still executing.\n",
2533 					               cr->host_name, cr->service_description);
2534 					if((svc = find_service(cr->host_name,
2535 					                       cr->service_description)) == NULL) {
2536 						logit(NSLOG_RUNTIME_WARNING, TRUE,
2537 						      "Warning: Check status contained service '%s' "
2538 						      "on host '%s', but the service could not be "
2539 						      "found! Ignoring check.\n",
2540 						      cr->service_description, cr->host_name);
2541 						}
2542 					else if(current_time - cr->start_time.tv_sec <
2543 					        service_check_timeout) {
2544 						log_debug_info(DEBUGL_CHECKS, 1,
2545 						               "Check for service %s:%s is still executing.\n",
2546 						               cr->host_name, cr->service_description);
2547 						svc->is_executing = TRUE;
2548 						}
2549 					}
2550 				free_check_result_list(&crl);
2551 				}
2552 			}
2553 		}
2554 
2555 	closedir(dirp);
2556 
2557 	return result;
2558 
2559 	}
2560 
2561 
2562 
2563 
2564 /* reads check result(s) from a file */
process_check_result_file(char * fname,check_result ** listp,int delete_file,int need_output)2565 int process_check_result_file(char *fname, check_result **listp, int delete_file, int need_output) {
2566 	mmapfile *thefile = NULL;
2567 	char *input = NULL;
2568 	char *var = NULL;
2569 	char *val = NULL;
2570 	char *v1 = NULL, *v2 = NULL;
2571 	time_t current_time;
2572 	check_result *new_cr = NULL;
2573 
2574 	if(fname == NULL)
2575 		return ERROR;
2576 
2577 	time(&current_time);
2578 
2579 	log_debug_info(DEBUGL_CHECKS, 1, "Processing check result file: '%s'\n", fname);
2580 
2581 	/* open the file for reading */
2582 	if((thefile = mmap_fopen(fname)) == NULL) {
2583 
2584 		/* try removing the file - zero length files can't be mmap()'ed, so it might exist */
2585 		unlink(fname);
2586 
2587 		return ERROR;
2588 		}
2589 
2590 	/* read in all lines from the file */
2591 	while(1) {
2592 
2593 		/* free memory */
2594 		my_free(input);
2595 
2596 		/* read the next line */
2597 		if((input = mmap_fgets_multiline(thefile)) == NULL)
2598 			break;
2599 
2600 		/* skip comments */
2601 		if(input[0] == '#')
2602 			continue;
2603 
2604 		/* empty line indicates end of record */
2605 		else if(input[0] == '\n') {
2606 
2607 			/* we have something... */
2608 			if(new_cr) {
2609 
2610 				/* do we have the minimum amount of data? */
2611 				if(new_cr->host_name != NULL &&
2612 				        (!need_output || new_cr->output != NULL)) {
2613 
2614 					/* add check result to list in memory */
2615 					add_check_result_to_list(listp, new_cr);
2616 
2617 					/* reset pointer */
2618 					new_cr = NULL;
2619 					}
2620 
2621 				/* discard partial input */
2622 				else {
2623 					free_check_result(new_cr);
2624 					init_check_result(new_cr);
2625 					new_cr->output_file = (char *)strdup(fname);
2626 					}
2627 				}
2628 			}
2629 
2630 		if((var = my_strtok(input, "=")) == NULL)
2631 			continue;
2632 		if((val = my_strtok(NULL, "\n")) == NULL)
2633 			continue;
2634 
2635 		/* found the file time */
2636 		if(!strcmp(var, "file_time")) {
2637 
2638 			/* file is too old - ignore check results it contains and delete it */
2639 			/* this will only work as intended if file_time comes before check results */
2640 			if(max_check_result_file_age > 0 && (current_time - (strtoul(val, NULL, 0)) > max_check_result_file_age)) {
2641 				delete_file = TRUE;
2642 				break;
2643 				}
2644 			}
2645 
2646 		/* else we have check result data */
2647 		else {
2648 
2649 			/* allocate new check result if necessary */
2650 			if(new_cr == NULL) {
2651 
2652 				if((new_cr = (check_result *)malloc(sizeof(check_result))) == NULL)
2653 					continue;
2654 
2655 				/* init values */
2656 				init_check_result(new_cr);
2657 				new_cr->output_file = (char *)strdup(fname);
2658 				}
2659 
2660 			if(!strcmp(var, "host_name"))
2661 				new_cr->host_name = (char *)strdup(val);
2662 			else if(!strcmp(var, "service_description")) {
2663 				new_cr->service_description = (char *)strdup(val);
2664 				new_cr->object_check_type = SERVICE_CHECK;
2665 				}
2666 			else if(!strcmp(var, "check_type"))
2667 				new_cr->check_type = atoi(val);
2668 			else if(!strcmp(var, "check_options"))
2669 				new_cr->check_options = atoi(val);
2670 			else if(!strcmp(var, "scheduled_check"))
2671 				new_cr->scheduled_check = atoi(val);
2672 			else if(!strcmp(var, "reschedule_check"))
2673 				new_cr->reschedule_check = atoi(val);
2674 			else if(!strcmp(var, "latency"))
2675 				new_cr->latency = strtod(val, NULL);
2676 			else if(!strcmp(var, "start_time")) {
2677 				if((v1 = strtok(val, ".")) == NULL)
2678 					continue;
2679 				if((v2 = strtok(NULL, "\n")) == NULL)
2680 					continue;
2681 				new_cr->start_time.tv_sec = strtoul(v1, NULL, 0);
2682 				new_cr->start_time.tv_usec = strtoul(v2, NULL, 0);
2683 				}
2684 			else if(!strcmp(var, "finish_time")) {
2685 				if((v1 = strtok(val, ".")) == NULL)
2686 					continue;
2687 				if((v2 = strtok(NULL, "\n")) == NULL)
2688 					continue;
2689 				new_cr->finish_time.tv_sec = strtoul(v1, NULL, 0);
2690 				new_cr->finish_time.tv_usec = strtoul(v2, NULL, 0);
2691 				}
2692 			else if(!strcmp(var, "early_timeout"))
2693 				new_cr->early_timeout = atoi(val);
2694 			else if(!strcmp(var, "exited_ok"))
2695 				new_cr->exited_ok = atoi(val);
2696 			else if(!strcmp(var, "return_code"))
2697 				new_cr->return_code = atoi(val);
2698 			else if(!strcmp(var, "output"))
2699 				new_cr->output = (char *)strdup(val);
2700 			}
2701 		}
2702 
2703 	/* we have something */
2704 	if(new_cr) {
2705 
2706 		/* do we have the minimum amount of data? */
2707 		if(new_cr->host_name != NULL &&
2708 		        (!need_output || new_cr->output != NULL)) {
2709 
2710 			/* add check result to list in memory */
2711 			add_check_result_to_list(listp, new_cr);
2712 
2713 			/* reset pointer */
2714 			new_cr = NULL;
2715 			}
2716 
2717 		/* discard partial input */
2718 		/* free memory for current check result record */
2719 		else {
2720 			free_check_result(new_cr);
2721 			my_free(new_cr);
2722 			}
2723 		}
2724 
2725 	/* free memory and close file */
2726 	my_free(input);
2727 	mmap_fclose(thefile);
2728 
2729 	/* delete the file (as well its ok-to-go file) if they exist */
2730 	if(TRUE == delete_file)
2731 		delete_check_result_file(fname);
2732 
2733 	return OK;
2734 	}
2735 
2736 
2737 
2738 
2739 /* deletes as check result file, as well as its ok-to-go file */
delete_check_result_file(char * fname)2740 int delete_check_result_file(char *fname) {
2741 	char *temp_buffer = NULL;
2742 
2743 	/* delete the result file */
2744 	unlink(fname);
2745 
2746 	/* delete the ok-to-go file */
2747 	asprintf(&temp_buffer, "%s.ok", fname);
2748 	unlink(temp_buffer);
2749 	my_free(temp_buffer);
2750 
2751 	return OK;
2752 	}
2753 
2754 
2755 
2756 
2757 /* reads the first host/service check result from the list in memory */
read_check_result(check_result ** listp)2758 check_result *read_check_result(check_result **listp) {
2759 	check_result *first_cr = NULL;
2760 
2761 	if(*listp == NULL)
2762 		return NULL;
2763 
2764 	first_cr = *listp;
2765 	*listp = (*listp)->next;
2766 
2767 	return first_cr;
2768 	}
2769 
2770 
2771 
2772 /* initializes a host/service check result */
init_check_result(check_result * info)2773 int init_check_result(check_result *info) {
2774 
2775 	if(info == NULL)
2776 		return ERROR;
2777 
2778 	/* reset vars */
2779 	info->object_check_type = HOST_CHECK;
2780 	info->host_name = NULL;
2781 	info->service_description = NULL;
2782 	info->check_type = HOST_CHECK_ACTIVE;
2783 	info->check_options = CHECK_OPTION_NONE;
2784 	info->scheduled_check = FALSE;
2785 	info->reschedule_check = FALSE;
2786 	info->output_file_fp = NULL;
2787 	info->output_file_fd = -1;
2788 	info->latency = 0.0;
2789 	info->start_time.tv_sec = 0;
2790 	info->start_time.tv_usec = 0;
2791 	info->finish_time.tv_sec = 0;
2792 	info->finish_time.tv_usec = 0;
2793 	info->early_timeout = FALSE;
2794 	info->exited_ok = TRUE;
2795 	info->return_code = 0;
2796 	info->output = NULL;
2797 	info->next = NULL;
2798 
2799 	return OK;
2800 	}
2801 
2802 
2803 
2804 
2805 /* adds a new host/service check result to the list in memory */
add_check_result_to_list(check_result ** listp,check_result * new_cr)2806 int add_check_result_to_list(check_result **listp, check_result *new_cr) {
2807 	check_result *temp_cr = NULL;
2808 	check_result *last_cr = NULL;
2809 
2810 	if(new_cr == NULL)
2811 		return ERROR;
2812 
2813 	/* add to list, sorted by finish time (asc) */
2814 
2815 	/* find insertion point */
2816 	last_cr = *listp;
2817 	for(temp_cr = *listp; temp_cr != NULL; temp_cr = temp_cr->next) {
2818 		if(temp_cr->finish_time.tv_sec >= new_cr->finish_time.tv_sec) {
2819 			if(temp_cr->finish_time.tv_sec > new_cr->finish_time.tv_sec)
2820 				break;
2821 			else if(temp_cr->finish_time.tv_usec > new_cr->finish_time.tv_usec)
2822 				break;
2823 			}
2824 		last_cr = temp_cr;
2825 		}
2826 
2827 	/* item goes at head of list */
2828 	if(*listp == NULL || temp_cr == *listp) {
2829 		new_cr->next = *listp;
2830 		*listp = new_cr;
2831 		}
2832 
2833 	/* item goes in middle or at end of list */
2834 	else {
2835 		new_cr->next = temp_cr;
2836 		last_cr->next = new_cr;
2837 		}
2838 
2839 	return OK;
2840 	}
2841 
2842 
2843 
2844 
2845 /* frees all memory associated with the check result list */
free_check_result_list(check_result ** listp)2846 int free_check_result_list(check_result **listp) {
2847 	check_result *this_cr = NULL;
2848 	check_result *next_cr = NULL;
2849 
2850 	for(this_cr = *listp; this_cr != NULL; this_cr = next_cr) {
2851 		next_cr = this_cr->next;
2852 		free_check_result(this_cr);
2853 		my_free(this_cr);
2854 		}
2855 
2856 	*listp = NULL;
2857 
2858 	return OK;
2859 	}
2860 
2861 
2862 
2863 
2864 /* frees memory associated with a host/service check result */
free_check_result(check_result * info)2865 int free_check_result(check_result *info) {
2866 
2867 	if(info == NULL)
2868 		return OK;
2869 
2870 	my_free(info->host_name);
2871 	my_free(info->service_description);
2872 	my_free(info->output_file);
2873 	my_free(info->output);
2874 
2875 	return OK;
2876 	}
2877 
2878 
2879 /* creates external command file as a named pipe (FIFO) and opens it for reading (non-blocked mode) */
open_command_file(void)2880 int open_command_file(void) {
2881 	struct stat st;
2882 	int result = 0;
2883 
2884 	/* if we're not checking external commands, don't do anything */
2885 	if(check_external_commands == FALSE)
2886 		return OK;
2887 
2888 	/* the command file was already created */
2889 	if(command_file_created == TRUE)
2890 		return OK;
2891 
2892 	/* reset umask (group needs write permissions) */
2893 	umask(S_IWOTH);
2894 
2895 	/* use existing FIFO if possible */
2896 	if(!(stat(command_file, &st) != -1 && (st.st_mode & S_IFIFO))) {
2897 
2898 		/* create the external command file as a named pipe (FIFO) */
2899 		if((result = mkfifo(command_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) != 0) {
2900 
2901 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Could not create external command file '%s' as named pipe: (%d) -> %s.  If this file already exists and you are sure that another copy of Nagios is not running, you should delete this file.\n", command_file, errno, strerror(errno));
2902 			return ERROR;
2903 			}
2904 		}
2905 
2906 	/* open the command file for reading (non-blocked) - O_TRUNC flag cannot be used due to errors on some systems */
2907 	/* NOTE: file must be opened read-write for poll() to work */
2908 	if((command_file_fd = open(command_file, O_RDWR | O_NONBLOCK)) < 0) {
2909 
2910 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Could not open external command file for reading via open(): (%d) -> %s\n", errno, strerror(errno));
2911 
2912 		return ERROR;
2913 		}
2914 
2915 	/* re-open the FIFO for use with fgets() */
2916 	if((command_file_fp = (FILE *)fdopen(command_file_fd, "r")) == NULL) {
2917 
2918 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Could not open external command file for reading via fdopen(): (%d) -> %s\n", errno, strerror(errno));
2919 
2920 		return ERROR;
2921 		}
2922 
2923 	/* initialize worker thread */
2924 	if(init_command_file_worker_thread() == ERROR) {
2925 
2926 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Could not initialize command file worker thread.\n");
2927 
2928 		/* close the command file */
2929 		fclose(command_file_fp);
2930 
2931 		/* delete the named pipe */
2932 		unlink(command_file);
2933 
2934 		return ERROR;
2935 		}
2936 
2937 	/* set a flag to remember we already created the file */
2938 	command_file_created = TRUE;
2939 
2940 	return OK;
2941 	}
2942 
2943 
2944 /* closes the external command file FIFO and deletes it */
close_command_file(void)2945 int close_command_file(void) {
2946 
2947 	/* if we're not checking external commands, don't do anything */
2948 	if(check_external_commands == FALSE)
2949 		return OK;
2950 
2951 	/* the command file wasn't created or was already cleaned up */
2952 	if(command_file_created == FALSE)
2953 		return OK;
2954 
2955 	/* reset our flag */
2956 	command_file_created = FALSE;
2957 
2958 	/* close the command file */
2959 	fclose(command_file_fp);
2960 
2961 	return OK;
2962 	}
2963 
2964 
2965 
2966 
2967 /******************************************************************/
2968 /************************ STRING FUNCTIONS ************************/
2969 /******************************************************************/
2970 
2971 /* gets the next string from a buffer in memory - strings are terminated by newlines, which are removed */
get_next_string_from_buf(char * buf,int * start_index,int bufsize)2972 char *get_next_string_from_buf(char *buf, int *start_index, int bufsize) {
2973 	char *sptr = NULL;
2974 	char *nl = "\n";
2975 	int x;
2976 
2977 	if(buf == NULL || start_index == NULL)
2978 		return NULL;
2979 	if(bufsize < 0)
2980 		return NULL;
2981 	if(*start_index >= (bufsize - 1))
2982 		return NULL;
2983 
2984 	sptr = buf + *start_index;
2985 
2986 	/* end of buffer */
2987 	if(sptr[0] == '\x0')
2988 		return NULL;
2989 
2990 	x = strcspn(sptr, nl);
2991 	sptr[x] = '\x0';
2992 
2993 	*start_index += x + 1;
2994 
2995 	return sptr;
2996 	}
2997 
2998 
2999 
3000 /* determines whether or not an object name (host, service, etc) contains illegal characters */
contains_illegal_object_chars(char * name)3001 int contains_illegal_object_chars(char *name) {
3002 	register int x = 0;
3003 	register int y = 0;
3004 	register int ch = 0;
3005 
3006 	if(name == NULL)
3007 		return FALSE;
3008 
3009 	x = (int)strlen(name) - 1;
3010 
3011 	for(; x >= 0; x--) {
3012 
3013 		ch = (int)name[x];
3014 
3015 		/* illegal user-specified characters */
3016 		if(illegal_object_chars != NULL)
3017 			for(y = 0; illegal_object_chars[y]; y++)
3018 				if(name[x] == illegal_object_chars[y])
3019 					return TRUE;
3020 		}
3021 
3022 	return FALSE;
3023 	}
3024 
3025 
3026 /* escapes newlines in a string */
escape_newlines(char * rawbuf)3027 char *escape_newlines(char *rawbuf) {
3028 	char *newbuf = NULL;
3029 	register int x, y;
3030 
3031 	if(rawbuf == NULL)
3032 		return NULL;
3033 
3034 	/* allocate enough memory to escape all chars if necessary */
3035 	if((newbuf = malloc((strlen(rawbuf) * 2) + 1)) == NULL)
3036 		return NULL;
3037 
3038 	for(x = 0, y = 0; rawbuf[x] != (char)'\x0'; x++) {
3039 
3040 		/* escape backslashes */
3041 		if(rawbuf[x] == '\\') {
3042 			newbuf[y++] = '\\';
3043 			newbuf[y++] = '\\';
3044 			}
3045 
3046 		/* escape newlines */
3047 		else if(rawbuf[x] == '\n') {
3048 			newbuf[y++] = '\\';
3049 			newbuf[y++] = 'n';
3050 			}
3051 
3052 		else
3053 			newbuf[y++] = rawbuf[x];
3054 		}
3055 	newbuf[y] = '\x0';
3056 
3057 	return newbuf;
3058 	}
3059 
3060 
3061 /* compares strings */
compare_strings(char * val1a,char * val2a)3062 int compare_strings(char *val1a, char *val2a) {
3063 
3064 	/* use the compare_hashdata() function */
3065 	return compare_hashdata(val1a, NULL, val2a, NULL);
3066 	}
3067 
3068 
3069 /******************************************************************/
3070 /************************* FILE FUNCTIONS *************************/
3071 /******************************************************************/
3072 
3073 /* renames a file - works across filesystems (Mike Wiacek) */
my_rename(char * source,char * dest)3074 int my_rename(char *source, char *dest) {
3075 	int rename_result = 0;
3076 
3077 
3078 	/* make sure we have something */
3079 	if(source == NULL || dest == NULL)
3080 		return -1;
3081 
3082 	/* first see if we can rename file with standard function */
3083 	rename_result = rename(source, dest);
3084 
3085 	/* handle any errors... */
3086 	if(rename_result == -1) {
3087 
3088 		/* an error occurred because the source and dest files are on different filesystems */
3089 		if(errno == EXDEV) {
3090 
3091 			/* try copying the file */
3092 			if(my_fcopy(source, dest) == ERROR) {
3093 				logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Unable to rename file '%s' to '%s': %s\n", source, dest, strerror(errno));
3094 				return -1;
3095 				}
3096 
3097 			/* delete the original file */
3098 			unlink(source);
3099 
3100 			/* reset result since we successfully copied file */
3101 			rename_result = 0;
3102 			}
3103 
3104 		/* some other error occurred */
3105 		else {
3106 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Unable to rename file '%s' to '%s': %s\n", source, dest, strerror(errno));
3107 			return rename_result;
3108 			}
3109 		}
3110 
3111 	return rename_result;
3112 	}
3113 
3114 /*
3115  * copy a file from the path at source to the already opened
3116  * destination file dest.
3117  * This is handy when creating tempfiles with mkstemp()
3118  */
my_fdcopy(char * source,char * dest,int dest_fd)3119 int my_fdcopy(char *source, char *dest, int dest_fd) {
3120 	int source_fd, rd_result = 0, wr_result = 0;
3121 	unsigned long tot_written = 0, tot_read = 0, buf_size = 0;
3122 	struct stat st;
3123 	char *buf;
3124 
3125 	/* open source file for reading */
3126 	if((source_fd = open(source, O_RDONLY, 0644)) < 0) {
3127 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Unable to open file '%s' for reading: %s\n", source, strerror(errno));
3128 		return ERROR;
3129 		}
3130 
3131 	/*
3132 	 * find out how large the source-file is so we can be sure
3133 	 * we've written all of it
3134 	 */
3135 	if(fstat(source_fd, &st) < 0) {
3136 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Unable to stat source file '%s' for my_fcopy(): %s\n", source, strerror(errno));
3137 		close(source_fd);
3138 		return ERROR;
3139 		}
3140 
3141 	/*
3142 	 * If the file is huge, read it and write it in chunks.
3143 	 * This value (128K) is the result of "pick-one-at-random"
3144 	 * with some minimal testing and may not be optimal for all
3145 	 * hardware setups, but it should work ok for most. It's
3146 	 * faster than 1K buffers and 1M buffers, so change at your
3147 	 * own peril. Note that it's useful to make it fit in the L2
3148 	 * cache, so larger isn't necessarily better.
3149 	 */
3150 	buf_size = st.st_size > 128 << 10 ? 128 << 10 : st.st_size;
3151 	buf = malloc(buf_size);
3152 	if(!buf) {
3153 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Unable to malloc(%lu) bytes: %s\n", buf_size, strerror(errno));
3154 		close(source_fd);
3155 		return ERROR;
3156 		}
3157 	/* most of the times, this loop will be gone through once */
3158 	while(tot_written < st.st_size) {
3159 		int loop_wr = 0;
3160 
3161 		rd_result = read(source_fd, buf, buf_size);
3162 		if(rd_result < 0) {
3163 			if(errno == EAGAIN || errno == EINTR)
3164 				continue;
3165 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: my_fcopy() failed to read from '%s': %s\n", source, strerror(errno));
3166 			break;
3167 			}
3168 		tot_read += rd_result;
3169 
3170 		while(loop_wr < rd_result) {
3171 			wr_result = write(dest_fd, buf + loop_wr, rd_result - loop_wr);
3172 
3173 			if(wr_result < 0) {
3174 				if(errno == EAGAIN || errno == EINTR)
3175 					continue;
3176 				logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: my_fcopy() failed to write to '%s': %s\n", dest, strerror(errno));
3177 				break;
3178 				}
3179 			loop_wr += wr_result;
3180 			}
3181 		if(wr_result < 0)
3182 			break;
3183 		tot_written += loop_wr;
3184 		}
3185 
3186 	/*
3187 	 * clean up irregardless of how things went. dest_fd comes from
3188 	 * our caller, so we mustn't close it.
3189 	 */
3190 	close(source_fd);
3191 	free(buf);
3192 
3193 	if(rd_result < 0 || wr_result < 0) {
3194 		/* don't leave half-written files around */
3195 		unlink(dest);
3196 		return ERROR;
3197 		}
3198 
3199 	return OK;
3200 	}
3201 
3202 /* copies a file */
my_fcopy(char * source,char * dest)3203 int my_fcopy(char *source, char *dest) {
3204 	int dest_fd, result;
3205 
3206 	/* make sure we have something */
3207 	if(source == NULL || dest == NULL)
3208 		return ERROR;
3209 
3210 	/* unlink destination file first (not doing so can cause problems on network file systems like CIFS) */
3211 	unlink(dest);
3212 
3213 	/* open destination file for writing */
3214 	if((dest_fd = open(dest, O_WRONLY | O_TRUNC | O_CREAT | O_APPEND, 0644)) < 0) {
3215 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Unable to open file '%s' for writing: %s\n", dest, strerror(errno));
3216 		return ERROR;
3217 		}
3218 
3219 	result = my_fdcopy(source, dest, dest_fd);
3220 	close(dest_fd);
3221 	return result;
3222 	}
3223 
3224 /******************************************************************/
3225 /******************** DYNAMIC BUFFER FUNCTIONS ********************/
3226 /******************************************************************/
3227 
3228 /* initializes a dynamic buffer */
dbuf_init(dbuf * db,int chunk_size)3229 int dbuf_init(dbuf *db, int chunk_size) {
3230 
3231 	if(db == NULL)
3232 		return ERROR;
3233 
3234 	db->buf = NULL;
3235 	db->used_size = 0L;
3236 	db->allocated_size = 0L;
3237 	db->chunk_size = chunk_size;
3238 
3239 	return OK;
3240 	}
3241 
3242 
3243 /* frees a dynamic buffer */
dbuf_free(dbuf * db)3244 int dbuf_free(dbuf *db) {
3245 
3246 	if(db == NULL)
3247 		return ERROR;
3248 
3249 	if(db->buf != NULL)
3250 		my_free(db->buf);
3251 	db->buf = NULL;
3252 	db->used_size = 0L;
3253 	db->allocated_size = 0L;
3254 
3255 	return OK;
3256 	}
3257 
3258 
3259 /* dynamically expands a string */
dbuf_strcat(dbuf * db,char * buf)3260 int dbuf_strcat(dbuf *db, char *buf) {
3261 	char *newbuf = NULL;
3262 	unsigned long buflen = 0L;
3263 	unsigned long new_size = 0L;
3264 	unsigned long memory_needed = 0L;
3265 
3266 	if(db == NULL || buf == NULL)
3267 		return ERROR;
3268 
3269 	/* how much memory should we allocate (if any)? */
3270 	buflen = strlen(buf);
3271 	new_size = db->used_size + buflen + 1;
3272 
3273 	/* we need more memory */
3274 	if(db->allocated_size < new_size) {
3275 
3276 		memory_needed = ((ceil(new_size / db->chunk_size) + 1) * db->chunk_size);
3277 
3278 		/* allocate memory to store old and new string */
3279 		if((newbuf = (char *)realloc((void *)db->buf, (size_t)memory_needed)) == NULL)
3280 			return ERROR;
3281 
3282 		/* update buffer pointer */
3283 		db->buf = newbuf;
3284 
3285 		/* update allocated size */
3286 		db->allocated_size = memory_needed;
3287 
3288 		/* terminate buffer */
3289 		db->buf[db->used_size] = '\x0';
3290 		}
3291 
3292 	/* append the new string */
3293 	strcat(db->buf, buf);
3294 
3295 	/* update size allocated */
3296 	db->used_size += buflen;
3297 
3298 	return OK;
3299 	}
3300 
3301 
3302 
3303 /******************************************************************/
3304 /******************** EMBEDDED PERL FUNCTIONS *********************/
3305 /******************************************************************/
3306 
3307 /* initializes embedded perl interpreter */
init_embedded_perl(char ** env)3308 int init_embedded_perl(char **env) {
3309 #ifdef EMBEDDEDPERL
3310 	char **embedding;
3311 	int exitstatus = 0;
3312 	int argc = 2;
3313 	struct stat stat_buf;
3314 
3315 	/* make sure the P1 file exists... */
3316 	if(p1_file == NULL || stat(p1_file, &stat_buf) != 0) {
3317 
3318 		use_embedded_perl = FALSE;
3319 
3320 		logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: p1.pl file required for embedded Perl interpreter is missing!\n");
3321 		}
3322 
3323 	else {
3324 
3325 		embedding = malloc(2 * sizeof(char *));
3326 		if(embedding == NULL)
3327 			return ERROR;
3328 		*embedding = strdup("");
3329 		*(embedding + 1) = strdup(p1_file);
3330 
3331 		use_embedded_perl = TRUE;
3332 
3333 		PERL_SYS_INIT3(&argc, &embedding, &env);
3334 
3335 		if((my_perl = perl_alloc()) == NULL) {
3336 			use_embedded_perl = FALSE;
3337 			logit(NSLOG_RUNTIME_ERROR, TRUE, "Error: Could not allocate memory for embedded Perl interpreter!\n");
3338 			}
3339 		}
3340 
3341 	/* a fatal error occurred... */
3342 	if(use_embedded_perl == FALSE) {
3343 
3344 		logit(NSLOG_PROCESS_INFO | NSLOG_RUNTIME_ERROR, TRUE, "Bailing out due to errors encountered while initializing the embedded Perl interpreter. (PID=%d)\n", (int)getpid());
3345 
3346 		cleanup();
3347 		exit(ERROR);
3348 		}
3349 
3350 	perl_construct(my_perl);
3351 	exitstatus = perl_parse(my_perl, xs_init, 2, (char **)embedding, env);
3352 	if(!exitstatus)
3353 		exitstatus = perl_run(my_perl);
3354 
3355 #endif
3356 	return OK;
3357 	}
3358 
3359 
3360 /* closes embedded perl interpreter */
deinit_embedded_perl(void)3361 int deinit_embedded_perl(void) {
3362 #ifdef EMBEDDEDPERL
3363 
3364 	PL_perl_destruct_level = 0;
3365 	perl_destruct(my_perl);
3366 	perl_free(my_perl);
3367 	PERL_SYS_TERM();
3368 
3369 #endif
3370 	return OK;
3371 	}
3372 
3373 
3374 /* checks to see if we should run a script using the embedded Perl interpreter */
file_uses_embedded_perl(char * fname)3375 int file_uses_embedded_perl(char *fname) {
3376 #ifndef EMBEDDEDPERL
3377 	return FALSE;
3378 #else
3379 	int line, use_epn = FALSE;
3380 	FILE *fp = NULL;
3381 	char buf[256] = "";
3382 
3383 	if(enable_embedded_perl != TRUE)
3384 		return FALSE;
3385 
3386 	/* open the file, check if its a Perl script and see if we can use epn  */
3387 	fp = fopen(fname, "r");
3388 	if(fp == NULL)
3389 		return FALSE;
3390 
3391 	/* grab the first line - we should see Perl. go home if not */
3392 	if(fgets(buf, 80, fp) == NULL || strstr(buf, "/bin/perl") == NULL) {
3393 		fclose(fp);
3394 		return FALSE;
3395 		}
3396 
3397 	/* epn directives must be found in first ten lines of plugin */
3398 	for(line = 1; line < 10; line++) {
3399 		if(fgets(buf, sizeof(buf) - 1, fp) == NULL)
3400 			break;
3401 
3402 		buf[sizeof(buf) - 1] = '\0';
3403 
3404 		/* skip lines not containing nagios 'epn' directives */
3405 		if(strstr(buf, "# nagios:")) {
3406 			char *p;
3407 			p = strstr(buf + 8, "epn");
3408 			if(!p)
3409 				continue;
3410 
3411 			/*
3412 			 * we found it, so close the file and return
3413 			 * whatever it shows. '+epn' means yes. everything
3414 			 * else means no
3415 			 */
3416 			fclose(fp);
3417 			return *(p - 1) == '+' ? TRUE : FALSE;
3418 			}
3419 		}
3420 
3421 	fclose(fp);
3422 
3423 	return use_embedded_perl_implicitly;
3424 #endif
3425 	}
3426 
3427 
3428 
3429 
3430 
3431 /******************************************************************/
3432 /************************ THREAD FUNCTIONS ************************/
3433 /******************************************************************/
3434 
3435 /* initializes command file worker thread */
init_command_file_worker_thread(void)3436 int init_command_file_worker_thread(void) {
3437 	int result = 0;
3438 	sigset_t newmask;
3439 
3440 	/* initialize circular buffer */
3441 	external_command_buffer.head = 0;
3442 	external_command_buffer.tail = 0;
3443 	external_command_buffer.items = 0;
3444 	external_command_buffer.high = 0;
3445 	external_command_buffer.overflow = 0L;
3446 	external_command_buffer.buffer = (void **)malloc(external_command_buffer_slots * sizeof(char **));
3447 	if(external_command_buffer.buffer == NULL)
3448 		return ERROR;
3449 
3450 	/* initialize mutex (only on cold startup) */
3451 	if(sigrestart == FALSE)
3452 		pthread_mutex_init(&external_command_buffer.buffer_lock, NULL);
3453 
3454 	/* new thread should block all signals */
3455 	sigfillset(&newmask);
3456 	pthread_sigmask(SIG_BLOCK, &newmask, NULL);
3457 
3458 	/* create worker thread */
3459 	result = pthread_create(&worker_threads[COMMAND_WORKER_THREAD], NULL, command_file_worker_thread, NULL);
3460 
3461 	/* main thread should unblock all signals */
3462 	pthread_sigmask(SIG_UNBLOCK, &newmask, NULL);
3463 
3464 	if(result)
3465 		return ERROR;
3466 
3467 	return OK;
3468 	}
3469 
3470 
3471 /* shutdown command file worker thread */
shutdown_command_file_worker_thread(void)3472 int shutdown_command_file_worker_thread(void) {
3473 	int result = 0;
3474 
3475 	/*
3476 	 * calling pthread_cancel(0) will cause segfaults with some
3477 	 * thread libraries. It's possible that will happen if the
3478 	 * user has a number of config files larger than the max
3479 	 * open file descriptor limit (ulimit -n) and some retarded
3480 	 * eventbroker module leaks filedescriptors, since we'll then
3481 	 * enter the cleanup() routine from main() before we've
3482 	 * spawned any threads.
3483 	 */
3484 	if(worker_threads[COMMAND_WORKER_THREAD]) {
3485 		/* tell the worker thread to exit */
3486 		result = pthread_cancel(worker_threads[COMMAND_WORKER_THREAD]);
3487 
3488 		/* wait for the worker thread to exit */
3489 		if(result == 0) {
3490 			result = pthread_join(worker_threads[COMMAND_WORKER_THREAD], NULL);
3491 			}
3492 
3493 		/* we're being called from a fork()'ed child process - can't cancel thread, so just cleanup memory */
3494 		else {
3495 			cleanup_command_file_worker_thread(NULL);
3496 			}
3497 		}
3498 
3499 	return OK;
3500 	}
3501 
3502 
3503 /* clean up resources used by command file worker thread */
cleanup_command_file_worker_thread(void * arg)3504 void cleanup_command_file_worker_thread(void *arg) {
3505 	register int x = 0;
3506 
3507 	/* release memory allocated to circular buffer */
3508 	for(x = external_command_buffer.tail; x != external_command_buffer.head; x = (x + 1) % external_command_buffer_slots) {
3509 		my_free(((char **)external_command_buffer.buffer)[x]);
3510 		}
3511 	my_free(external_command_buffer.buffer);
3512 
3513 	return;
3514 	}
3515 
3516 
3517 
3518 /* worker thread - artificially increases buffer of named pipe */
command_file_worker_thread(void * arg)3519 void * command_file_worker_thread(void *arg) {
3520 	char input_buffer[MAX_EXTERNAL_COMMAND_LENGTH];
3521 	struct pollfd pfd;
3522 	int pollval;
3523 	struct timeval tv;
3524 	int buffer_items = 0;
3525 	int result = 0;
3526 
3527 	/* specify cleanup routine */
3528 	pthread_cleanup_push(cleanup_command_file_worker_thread, NULL);
3529 
3530 	/* set cancellation info */
3531 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
3532 	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
3533 
3534 	while(1) {
3535 
3536 		/* should we shutdown? */
3537 		pthread_testcancel();
3538 
3539 		/* wait for data to arrive */
3540 		/* select seems to not work, so we have to use poll instead */
3541 		/* 10-15-08 EG check into implementing William's patch @ http://blog.netways.de/2008/08/15/nagios-unter-mac-os-x-installieren/ */
3542 		/* 10-15-08 EG poll() seems broken on OSX - see Jonathan's patch a few lines down */
3543 		pfd.fd = command_file_fd;
3544 		pfd.events = POLLIN;
3545 		pollval = poll(&pfd, 1, 500);
3546 
3547 		/* loop if no data */
3548 		if(pollval == 0)
3549 			continue;
3550 
3551 		/* check for errors */
3552 		if(pollval == -1) {
3553 
3554 			switch(errno) {
3555 				case EBADF:
3556 					write_to_log("command_file_worker_thread(): poll(): EBADF", logging_options, NULL);
3557 					break;
3558 				case ENOMEM:
3559 					write_to_log("command_file_worker_thread(): poll(): ENOMEM", logging_options, NULL);
3560 					break;
3561 				case EFAULT:
3562 					write_to_log("command_file_worker_thread(): poll(): EFAULT", logging_options, NULL);
3563 					break;
3564 				case EINTR:
3565 					/* this can happen when running under a debugger like gdb */
3566 					/*
3567 					write_to_log("command_file_worker_thread(): poll(): EINTR (impossible)",logging_options,NULL);
3568 					*/
3569 					break;
3570 				default:
3571 					write_to_log("command_file_worker_thread(): poll(): Unknown errno value.", logging_options, NULL);
3572 					break;
3573 				}
3574 
3575 			continue;
3576 			}
3577 
3578 		/* should we shutdown? */
3579 		pthread_testcancel();
3580 
3581 		/* get number of items in the buffer */
3582 		pthread_mutex_lock(&external_command_buffer.buffer_lock);
3583 		buffer_items = external_command_buffer.items;
3584 		pthread_mutex_unlock(&external_command_buffer.buffer_lock);
3585 
3586 #ifdef DEBUG_CFWT
3587 		printf("(CFWT) BUFFER ITEMS: %d/%d\n", buffer_items, external_command_buffer_slots);
3588 #endif
3589 
3590 		/* 10-15-08 Fix for OS X by Jonathan Saggau - see http://www.jonathansaggau.com/blog/2008/09/using_shark_and_custom_dtrace.html */
3591 		/* Not sure if this would have negative effects on other OSes... */
3592 		if(buffer_items == 0) {
3593 			/* pause a bit so OS X doesn't go nuts with CPU overload */
3594 			tv.tv_sec = 0;
3595 			tv.tv_usec = 500;
3596 			select(0, NULL, NULL, NULL, &tv);
3597 			}
3598 
3599 		/* process all commands in the file (named pipe) if there's some space in the buffer */
3600 		if(buffer_items < external_command_buffer_slots) {
3601 
3602 			/* clear EOF condition from prior run (FreeBSD fix) */
3603 			/* FIXME: use_poll_on_cmd_pipe: Still needed? */
3604 			clearerr(command_file_fp);
3605 
3606 			/* read and process the next command in the file */
3607 			while(fgets(input_buffer, (int)(sizeof(input_buffer) - 1), command_file_fp) != NULL) {
3608 
3609 #ifdef DEBUG_CFWT
3610 				printf("(CFWT) READ: %s", input_buffer);
3611 #endif
3612 
3613 				/* submit the external command for processing (retry if buffer is full) */
3614 				while((result = submit_external_command(input_buffer, &buffer_items)) == ERROR && buffer_items == external_command_buffer_slots) {
3615 
3616 					/* wait a bit */
3617 					tv.tv_sec = 0;
3618 					tv.tv_usec = 250000;
3619 					select(0, NULL, NULL, NULL, &tv);
3620 
3621 					/* should we shutdown? */
3622 					pthread_testcancel();
3623 					}
3624 
3625 #ifdef DEBUG_CFWT
3626 				printf("(CFWT) RES: %d, BUFFER_ITEMS: %d/%d\n", result, buffer_items, external_comand_buffer_slots);
3627 #endif
3628 
3629 				/* bail if the circular buffer is full */
3630 				if(buffer_items == external_command_buffer_slots)
3631 					break;
3632 
3633 				/* should we shutdown? */
3634 				pthread_testcancel();
3635 				}
3636 			}
3637 		}
3638 
3639 	/* removes cleanup handler - this should never be reached */
3640 	pthread_cleanup_pop(0);
3641 
3642 	return NULL;
3643 	}
3644 
3645 
3646 
3647 /* submits an external command for processing */
submit_external_command(char * cmd,int * buffer_items)3648 int submit_external_command(char *cmd, int *buffer_items) {
3649 	int result = OK;
3650 
3651 	if(cmd == NULL || external_command_buffer.buffer == NULL) {
3652 		if(buffer_items != NULL)
3653 			*buffer_items = -1;
3654 		return ERROR;
3655 		}
3656 
3657 	/* obtain a lock for writing to the buffer */
3658 	pthread_mutex_lock(&external_command_buffer.buffer_lock);
3659 
3660 	if(external_command_buffer.items < external_command_buffer_slots) {
3661 
3662 		/* save the line in the buffer */
3663 		((char **)external_command_buffer.buffer)[external_command_buffer.head] = (char *)strdup(cmd);
3664 
3665 		/* increment the head counter and items */
3666 		external_command_buffer.head = (external_command_buffer.head + 1) % external_command_buffer_slots;
3667 		external_command_buffer.items++;
3668 		if(external_command_buffer.items > external_command_buffer.high)
3669 			external_command_buffer.high = external_command_buffer.items;
3670 		}
3671 
3672 	/* buffer was full */
3673 	else
3674 		result = ERROR;
3675 
3676 	/* return number of items now in buffer */
3677 	if(buffer_items != NULL)
3678 		*buffer_items = external_command_buffer.items;
3679 
3680 	/* release lock on buffer */
3681 	pthread_mutex_unlock(&external_command_buffer.buffer_lock);
3682 
3683 	return result;
3684 	}
3685 
3686 
3687 
3688 /* submits a raw external command (without timestamp) for processing */
submit_raw_external_command(char * cmd,time_t * ts,int * buffer_items)3689 int submit_raw_external_command(char *cmd, time_t *ts, int *buffer_items) {
3690 	char *newcmd = NULL;
3691 	int result = OK;
3692 	time_t timestamp;
3693 
3694 	if(cmd == NULL)
3695 		return ERROR;
3696 
3697 	/* get the time */
3698 	if(ts != NULL)
3699 		timestamp = *ts;
3700 	else
3701 		time(&timestamp);
3702 
3703 	/* create the command string */
3704 	asprintf(&newcmd, "[%lu] %s", (unsigned long)timestamp, cmd);
3705 
3706 	/* submit the command */
3707 	result = submit_external_command(newcmd, buffer_items);
3708 
3709 	/* free allocated memory */
3710 	my_free(newcmd);
3711 
3712 	return result;
3713 	}
3714 
3715 
3716 
3717 /******************************************************************/
3718 /********************** CHECK STATS FUNCTIONS *********************/
3719 /******************************************************************/
3720 
3721 /* initialize check statistics data structures */
init_check_stats(void)3722 int init_check_stats(void) {
3723 	int x = 0;
3724 	int y = 0;
3725 
3726 	for(x = 0; x < MAX_CHECK_STATS_TYPES; x++) {
3727 		check_statistics[x].current_bucket = 0;
3728 		for(y = 0; y < CHECK_STATS_BUCKETS; y++)
3729 			check_statistics[x].bucket[y] = 0;
3730 		check_statistics[x].overflow_bucket = 0;
3731 		for(y = 0; y < 3; y++)
3732 			check_statistics[x].minute_stats[y] = 0;
3733 		check_statistics[x].last_update = (time_t)0L;
3734 		}
3735 
3736 	return OK;
3737 	}
3738 
3739 
3740 /* records stats for a given type of check */
update_check_stats(int check_type,time_t check_time)3741 int update_check_stats(int check_type, time_t check_time) {
3742 	time_t current_time;
3743 	unsigned long minutes = 0L;
3744 	int new_current_bucket = 0;
3745 	int this_bucket = 0;
3746 	int x = 0;
3747 
3748 	if(check_type < 0 || check_type >= MAX_CHECK_STATS_TYPES)
3749 		return ERROR;
3750 
3751 	time(&current_time);
3752 
3753 	if((unsigned long)check_time == 0L) {
3754 #ifdef DEBUG_CHECK_STATS
3755 		printf("TYPE[%d] CHECK TIME==0!\n", check_type);
3756 #endif
3757 		check_time = current_time;
3758 		}
3759 
3760 	/* do some sanity checks on the age of the stats data before we start... */
3761 	/* get the new current bucket number */
3762 	minutes = ((unsigned long)check_time - (unsigned long)program_start) / 60;
3763 	new_current_bucket = minutes % CHECK_STATS_BUCKETS;
3764 
3765 	/* its been more than 15 minutes since stats were updated, so clear the stats */
3766 	if((((unsigned long)current_time - (unsigned long)check_statistics[check_type].last_update) / 60) > CHECK_STATS_BUCKETS) {
3767 		for(x = 0; x < CHECK_STATS_BUCKETS; x++)
3768 			check_statistics[check_type].bucket[x] = 0;
3769 		check_statistics[check_type].overflow_bucket = 0;
3770 #ifdef DEBUG_CHECK_STATS
3771 		printf("CLEARING ALL: TYPE[%d], CURRENT=%lu, LASTUPDATE=%lu\n", check_type, (unsigned long)current_time, (unsigned long)check_statistics[check_type].last_update);
3772 #endif
3773 		}
3774 
3775 	/* different current bucket number than last time */
3776 	else if(new_current_bucket != check_statistics[check_type].current_bucket) {
3777 
3778 		/* clear stats in buckets between last current bucket and new current bucket - stats haven't been updated in a while */
3779 		for(x = check_statistics[check_type].current_bucket; x < (CHECK_STATS_BUCKETS * 2); x++) {
3780 
3781 			this_bucket = (x + CHECK_STATS_BUCKETS + 1) % CHECK_STATS_BUCKETS;
3782 
3783 			if(this_bucket == new_current_bucket)
3784 				break;
3785 
3786 #ifdef DEBUG_CHECK_STATS
3787 			printf("CLEARING BUCKET %d, (NEW=%d, OLD=%d)\n", this_bucket, new_current_bucket, check_statistics[check_type].current_bucket);
3788 #endif
3789 
3790 			/* clear old bucket value */
3791 			check_statistics[check_type].bucket[this_bucket] = 0;
3792 			}
3793 
3794 		/* update the current bucket number, push old value to overflow bucket */
3795 		check_statistics[check_type].overflow_bucket = check_statistics[check_type].bucket[new_current_bucket];
3796 		check_statistics[check_type].current_bucket = new_current_bucket;
3797 		check_statistics[check_type].bucket[new_current_bucket] = 0;
3798 		}
3799 #ifdef DEBUG_CHECK_STATS
3800 	else
3801 		printf("NO CLEARING NEEDED\n");
3802 #endif
3803 
3804 
3805 	/* increment the value of the current bucket */
3806 	check_statistics[check_type].bucket[new_current_bucket]++;
3807 
3808 #ifdef DEBUG_CHECK_STATS
3809 	printf("TYPE[%d].BUCKET[%d]=%d\n", check_type, new_current_bucket, check_statistics[check_type].bucket[new_current_bucket]);
3810 	printf("   ");
3811 	for(x = 0; x < CHECK_STATS_BUCKETS; x++)
3812 		printf("[%d] ", check_statistics[check_type].bucket[x]);
3813 	printf(" (%d)\n", check_statistics[check_type].overflow_bucket);
3814 #endif
3815 
3816 	/* record last update time */
3817 	check_statistics[check_type].last_update = current_time;
3818 
3819 	return OK;
3820 	}
3821 
3822 
3823 /* generate 1/5/15 minute stats for a given type of check */
generate_check_stats(void)3824 int generate_check_stats(void) {
3825 	time_t current_time;
3826 	int x = 0;
3827 	int new_current_bucket = 0;
3828 	int this_bucket = 0;
3829 	int last_bucket = 0;
3830 	int this_bucket_value = 0;
3831 	int last_bucket_value = 0;
3832 	int bucket_value = 0;
3833 	int seconds = 0;
3834 	int minutes = 0;
3835 	int check_type = 0;
3836 	float this_bucket_weight = 0.0;
3837 	float last_bucket_weight = 0.0;
3838 	int left_value = 0;
3839 	int right_value = 0;
3840 
3841 
3842 	time(&current_time);
3843 
3844 	/* do some sanity checks on the age of the stats data before we start... */
3845 	/* get the new current bucket number */
3846 	minutes = ((unsigned long)current_time - (unsigned long)program_start) / 60;
3847 	new_current_bucket = minutes % CHECK_STATS_BUCKETS;
3848 	for(check_type = 0; check_type < MAX_CHECK_STATS_TYPES; check_type++) {
3849 
3850 		/* its been more than 15 minutes since stats were updated, so clear the stats */
3851 		if((((unsigned long)current_time - (unsigned long)check_statistics[check_type].last_update) / 60) > CHECK_STATS_BUCKETS) {
3852 			for(x = 0; x < CHECK_STATS_BUCKETS; x++)
3853 				check_statistics[check_type].bucket[x] = 0;
3854 			check_statistics[check_type].overflow_bucket = 0;
3855 #ifdef DEBUG_CHECK_STATS
3856 			printf("GEN CLEARING ALL: TYPE[%d], CURRENT=%lu, LASTUPDATE=%lu\n", check_type, (unsigned long)current_time, (unsigned long)check_statistics[check_type].last_update);
3857 #endif
3858 			}
3859 
3860 		/* different current bucket number than last time */
3861 		else if(new_current_bucket != check_statistics[check_type].current_bucket) {
3862 
3863 			/* clear stats in buckets between last current bucket and new current bucket - stats haven't been updated in a while */
3864 			for(x = check_statistics[check_type].current_bucket; x < (CHECK_STATS_BUCKETS * 2); x++) {
3865 
3866 				this_bucket = (x + CHECK_STATS_BUCKETS + 1) % CHECK_STATS_BUCKETS;
3867 
3868 				if(this_bucket == new_current_bucket)
3869 					break;
3870 
3871 #ifdef DEBUG_CHECK_STATS
3872 				printf("GEN CLEARING BUCKET %d, (NEW=%d, OLD=%d), CURRENT=%lu, LASTUPDATE=%lu\n", this_bucket, new_current_bucket, check_statistics[check_type].current_bucket, (unsigned long)current_time, (unsigned long)check_statistics[check_type].last_update);
3873 #endif
3874 
3875 				/* clear old bucket value */
3876 				check_statistics[check_type].bucket[this_bucket] = 0;
3877 				}
3878 
3879 			/* update the current bucket number, push old value to overflow bucket */
3880 			check_statistics[check_type].overflow_bucket = check_statistics[check_type].bucket[new_current_bucket];
3881 			check_statistics[check_type].current_bucket = new_current_bucket;
3882 			check_statistics[check_type].bucket[new_current_bucket] = 0;
3883 			}
3884 #ifdef DEBUG_CHECK_STATS
3885 		else
3886 			printf("GEN NO CLEARING NEEDED: TYPE[%d], CURRENT=%lu, LASTUPDATE=%lu\n", check_type, (unsigned long)current_time, (unsigned long)check_statistics[check_type].last_update);
3887 #endif
3888 
3889 		/* update last check time */
3890 		check_statistics[check_type].last_update = current_time;
3891 		}
3892 
3893 	/* determine weights to use for this/last buckets */
3894 	seconds = ((unsigned long)current_time - (unsigned long)program_start) % 60;
3895 	this_bucket_weight = (seconds / 60.0);
3896 	last_bucket_weight = ((60 - seconds) / 60.0);
3897 
3898 	/* update statistics for all check types */
3899 	for(check_type = 0; check_type < MAX_CHECK_STATS_TYPES; check_type++) {
3900 
3901 		/* clear the old statistics */
3902 		for(x = 0; x < 3; x++)
3903 			check_statistics[check_type].minute_stats[x] = 0;
3904 
3905 		/* loop through each bucket */
3906 		for(x = 0; x < CHECK_STATS_BUCKETS; x++) {
3907 
3908 			/* which buckets should we use for this/last bucket? */
3909 			this_bucket = (check_statistics[check_type].current_bucket + CHECK_STATS_BUCKETS - x) % CHECK_STATS_BUCKETS;
3910 			last_bucket = (this_bucket + CHECK_STATS_BUCKETS - 1) % CHECK_STATS_BUCKETS;
3911 
3912 			/* raw/unweighted value for this bucket */
3913 			this_bucket_value = check_statistics[check_type].bucket[this_bucket];
3914 
3915 			/* raw/unweighted value for last bucket - use overflow bucket if last bucket is current bucket */
3916 			if(last_bucket == check_statistics[check_type].current_bucket)
3917 				last_bucket_value = check_statistics[check_type].overflow_bucket;
3918 			else
3919 				last_bucket_value = check_statistics[check_type].bucket[last_bucket];
3920 
3921 			/* determine value by weighting this/last buckets... */
3922 			/* if this is the current bucket, use its full value + weighted % of last bucket */
3923 			if(x == 0) {
3924 				right_value = this_bucket_value;
3925 				left_value = (int)floor(last_bucket_value * last_bucket_weight);
3926 				bucket_value = (int)(this_bucket_value + floor(last_bucket_value * last_bucket_weight));
3927 				}
3928 			/* otherwise use weighted % of this and last bucket */
3929 			else {
3930 				right_value = (int)ceil(this_bucket_value * this_bucket_weight);
3931 				left_value = (int)floor(last_bucket_value * last_bucket_weight);
3932 				bucket_value = (int)(ceil(this_bucket_value * this_bucket_weight) + floor(last_bucket_value * last_bucket_weight));
3933 				}
3934 
3935 			/* 1 minute stats */
3936 			if(x == 0)
3937 				check_statistics[check_type].minute_stats[0] = bucket_value;
3938 
3939 			/* 5 minute stats */
3940 			if(x < 5)
3941 				check_statistics[check_type].minute_stats[1] += bucket_value;
3942 
3943 			/* 15 minute stats */
3944 			if(x < 15)
3945 				check_statistics[check_type].minute_stats[2] += bucket_value;
3946 
3947 #ifdef DEBUG_CHECK_STATS2
3948 			printf("X=%d, THIS[%d]=%d, LAST[%d]=%d, 1/5/15=%d,%d,%d  L=%d R=%d\n", x, this_bucket, this_bucket_value, last_bucket, last_bucket_value, check_statistics[check_type].minute_stats[0], check_statistics[check_type].minute_stats[1], check_statistics[check_type].minute_stats[2], left_value, right_value);
3949 #endif
3950 			/* record last update time */
3951 			check_statistics[check_type].last_update = current_time;
3952 			}
3953 
3954 #ifdef DEBUG_CHECK_STATS
3955 		printf("TYPE[%d]   1/5/15 = %d, %d, %d (seconds=%d, this_weight=%f, last_weight=%f)\n", check_type, check_statistics[check_type].minute_stats[0], check_statistics[check_type].minute_stats[1], check_statistics[check_type].minute_stats[2], seconds, this_bucket_weight, last_bucket_weight);
3956 #endif
3957 		}
3958 
3959 	return OK;
3960 	}
3961 
3962 
3963 
3964 
3965 /******************************************************************/
3966 /************************ UPDATE FUNCTIONS ************************/
3967 /******************************************************************/
3968 
3969 /* check for new releases of Nagios */
check_for_nagios_updates(int force,int reschedule)3970 int check_for_nagios_updates(int force, int reschedule) {
3971 	time_t current_time;
3972 	int result = OK;
3973 	int api_result = OK;
3974 	int do_check = TRUE;
3975 	time_t next_check = 0L;
3976 	unsigned int rand_seed = 0;
3977 	int randnum = 0;
3978 
3979 	time(&current_time);
3980 
3981 	/*
3982 	printf("NOW: %s",ctime(&current_time));
3983 	printf("LAST CHECK: %s",ctime(&last_update_check));
3984 	*/
3985 
3986 	/* seed the random generator */
3987 	rand_seed = (unsigned int)(current_time + nagios_pid);
3988 	srand(rand_seed);
3989 
3990 	/* generate a (probably) unique ID for this nagios install */
3991 	/* the server api currently sees thousands of nagios installs behind single ip addresses, so this help determine if there are really thousands of servers out there, or if some nagios installs are misbehaving */
3992 	if(update_uid == 0L)
3993 		update_uid = current_time;
3994 
3995 	/* update chekcs are disabled */
3996 	if(check_for_updates == FALSE)
3997 		do_check = FALSE;
3998 	/* we checked for updates recently, so don't do it again */
3999 	if((current_time - last_update_check) < MINIMUM_UPDATE_CHECK_INTERVAL)
4000 		do_check = FALSE;
4001 	/* the check is being forced */
4002 	if(force == TRUE)
4003 		do_check = TRUE;
4004 
4005 	/* do a check */
4006 	if(do_check == TRUE) {
4007 
4008 		/*printf("RUNNING QUERY...\n");*/
4009 
4010 		/* query api */
4011 		api_result = query_update_api();
4012 		}
4013 
4014 	/* should we reschedule the update check? */
4015 	if(reschedule == TRUE) {
4016 
4017 		/*printf("RESCHEDULING...\n");*/
4018 
4019 		randnum = rand();
4020 		/*
4021 		printf("RAND: %d\n",randnum);
4022 		printf("RANDMAX: %d\n",RAND_MAX);
4023 		printf("UCIW: %d\n",UPDATE_CHECK_INTERVAL_WOBBLE);
4024 		printf("MULT: %f\n",(float)randnum/RAND_MAX);
4025 		*/
4026 
4027 
4028 
4029 		/* we didn't do an update, so calculate next possible update time */
4030 		if(do_check == FALSE) {
4031 			next_check = last_update_check + BASE_UPDATE_CHECK_INTERVAL;
4032 			next_check = next_check + (unsigned long)(((float)randnum / RAND_MAX) * UPDATE_CHECK_INTERVAL_WOBBLE);
4033 			}
4034 
4035 		/* we tried to check for an update */
4036 		else {
4037 
4038 			/* api query was okay */
4039 			if(api_result == OK) {
4040 				next_check = current_time + BASE_UPDATE_CHECK_INTERVAL;
4041 				next_check += (unsigned long)(((float)randnum / RAND_MAX) * UPDATE_CHECK_INTERVAL_WOBBLE);
4042 				}
4043 
4044 			/* query resulted in an error - retry at a shorter interval */
4045 			else {
4046 				next_check = current_time + BASE_UPDATE_CHECK_RETRY_INTERVAL;
4047 				next_check += (unsigned long)(((float)randnum / RAND_MAX) * UPDATE_CHECK_RETRY_INTERVAL_WOBBLE);
4048 				}
4049 			}
4050 
4051 		/* make sure next check isn't in the past - if it is, schedule a check in 1 minute */
4052 		if(next_check < current_time)
4053 			next_check = current_time + 60;
4054 
4055 		/*printf("NEXT CHECK: %s",ctime(&next_check));*/
4056 
4057 		/* schedule the next update event */
4058 		schedule_new_event(EVENT_CHECK_PROGRAM_UPDATE, TRUE, next_check, FALSE, BASE_UPDATE_CHECK_INTERVAL, NULL, TRUE, NULL, NULL, 0);
4059 		}
4060 
4061 	return result;
4062 	}
4063 
4064 
4065 
4066 /* checks for updates at api.nagios.org */
query_update_api(void)4067 int query_update_api(void) {
4068 	char *api_server = "api.nagios.org";
4069 	char *api_path = "/versioncheck/";
4070 	char *api_query = NULL;
4071 	char *api_query_opts = NULL;
4072 	char *buf = NULL;
4073 	char recv_buf[1024];
4074 	int report_install = FALSE;
4075 	int result = OK;
4076 	char *ptr = NULL;
4077 	int current_line = 0;
4078 	int buf_index = 0;
4079 	int in_header = TRUE;
4080 	char *var = NULL;
4081 	char *val = NULL;
4082 	int sd = 0;
4083 	int send_len = 0;
4084 	int recv_len = 0;
4085 	int update_check_succeeded = FALSE;
4086 
4087 	/* report a new install, upgrade, or rollback */
4088 	/* Nagios monitors the world and we monitor Nagios taking over the world. :-) */
4089 	if(last_update_check == (time_t)0L)
4090 		report_install = TRUE;
4091 	if(last_program_version == NULL || strcmp(PROGRAM_VERSION, last_program_version))
4092 		report_install = TRUE;
4093 	if(report_install == TRUE) {
4094 		asprintf(&api_query_opts, "&firstcheck=1");
4095 		if(last_program_version != NULL) {
4096 			char *qopts2 = NULL;
4097 			asprintf(&qopts2, "%s&last_version=%s", api_query_opts, last_program_version);
4098 			my_free(api_query_opts);
4099 			api_query_opts = qopts2;
4100 			}
4101 		}
4102 
4103 	/* generate the query */
4104 	asprintf(&api_query, "v=1&product=nagios&tinycheck=1&stableonly=1&uid=%lu", update_uid);
4105 	if(bare_update_check == FALSE) {
4106 		char *api_query2 = NULL;
4107 		asprintf(&api_query2, "%s&version=%s%s", api_query, PROGRAM_VERSION, (api_query_opts == NULL) ? "" : api_query_opts);
4108 		my_free(api_query);
4109 		api_query = api_query2;
4110 		}
4111 
4112 	/* generate the HTTP request */
4113 	asprintf(&buf,
4114 	         "POST %s HTTP/1.0\r\nUser-Agent: Nagios/%s\r\n"
4115 	         "Connection: close\r\nHost: %s\r\n"
4116 	         "Content-Type: application/x-www-form-urlencoded\r\n"
4117 	         "Content-Length: %zd\r\n\r\n%s",
4118 	         api_path, PROGRAM_VERSION, api_server,
4119 	         strlen(api_query), api_query);
4120 
4121 	/*
4122 	printf("SENDING...\n");
4123 	printf("==========\n");
4124 	printf("%s",buf);
4125 	printf("\n");
4126 	*/
4127 
4128 
4129 	result = my_tcp_connect(api_server, 80, &sd, 2);
4130 	/*printf("CONN RESULT: %d, SD: %d\n",result,sd);*/
4131 	if(sd > 0) {
4132 
4133 		/* send request */
4134 		send_len = strlen(buf);
4135 		result = my_sendall(sd, buf, &send_len, 2);
4136 		/*printf("SEND RESULT: %d, SENT: %d\n",result,send_len);*/
4137 
4138 		/* get response */
4139 		recv_len = sizeof(recv_buf);
4140 		result = my_recvall(sd, recv_buf, &recv_len, 2);
4141 		recv_buf[sizeof(recv_buf) - 1] = '\x0';
4142 		/*printf("RECV RESULT: %d, RECEIVED: %d\n",result,recv_len);*/
4143 
4144 		/*
4145 		printf("\n");
4146 		printf("RECEIVED...\n");
4147 		printf("===========\n");
4148 		printf("%s",recv_buf);
4149 		printf("\n");
4150 		*/
4151 
4152 		/* close connection */
4153 		close(sd);
4154 
4155 		/* parse the result */
4156 		in_header = TRUE;
4157 		while((ptr = get_next_string_from_buf(recv_buf, &buf_index, sizeof(recv_buf)))) {
4158 
4159 			strip(ptr);
4160 			current_line++;
4161 
4162 			if(!strcmp(ptr, "")) {
4163 				in_header = FALSE;
4164 				continue;
4165 				}
4166 			if(in_header == TRUE)
4167 				continue;
4168 
4169 			var = strtok(ptr, "=");
4170 			val = strtok(NULL, "\n");
4171 			/*printf("VAR: %s, VAL: %s\n",var,val);*/
4172 
4173 			if(!strcmp(var, "UPDATE_AVAILABLE")) {
4174 				update_available = atoi(val);
4175 				/* we were successful */
4176 				update_check_succeeded = TRUE;
4177 				}
4178 			else if(!strcmp(var, "UPDATE_VERSION")) {
4179 				if(new_program_version)
4180 					my_free(new_program_version);
4181 				new_program_version = strdup(val);
4182 				}
4183 			else if(!strcmp(var, "UPDATE_RELEASEDATE")) {
4184 				}
4185 			}
4186 		}
4187 
4188 	/* cleanup */
4189 	my_free(buf);
4190 	my_free(api_query);
4191 	my_free(api_query_opts);
4192 
4193 	/* we were successful! */
4194 	if(update_check_succeeded == TRUE) {
4195 
4196 		time(&last_update_check);
4197 		if(last_program_version)
4198 			free(last_program_version);
4199 		last_program_version = (char *)strdup(PROGRAM_VERSION);
4200 		}
4201 
4202 	return OK;
4203 	}
4204 
4205 
4206 
4207 
4208 /******************************************************************/
4209 /************************* MISC FUNCTIONS *************************/
4210 /******************************************************************/
4211 
4212 /* returns Nagios version */
get_program_version(void)4213 char *get_program_version(void) {
4214 
4215 	return (char *)PROGRAM_VERSION;
4216 	}
4217 
4218 
4219 /* returns Nagios modification date */
get_program_modification_date(void)4220 char *get_program_modification_date(void) {
4221 
4222 	return (char *)PROGRAM_MODIFICATION_DATE;
4223 	}
4224 
4225 
4226 
4227 /******************************************************************/
4228 /*********************** CLEANUP FUNCTIONS ************************/
4229 /******************************************************************/
4230 
4231 /* do some cleanup before we exit */
cleanup(void)4232 void cleanup(void) {
4233 
4234 #ifdef USE_EVENT_BROKER
4235 	/* unload modules */
4236 	if(test_scheduling == FALSE && verify_config == FALSE) {
4237 		neb_free_callback_list();
4238 		neb_unload_all_modules(NEBMODULE_FORCE_UNLOAD, (sigshutdown == TRUE) ? NEBMODULE_NEB_SHUTDOWN : NEBMODULE_NEB_RESTART);
4239 		neb_free_module_list();
4240 		neb_deinit_modules();
4241 		}
4242 #endif
4243 
4244 	/* free all allocated memory - including macros */
4245 	free_memory(get_global_macros());
4246 
4247 	return;
4248 	}
4249 
4250 
4251 /* free the memory allocated to the linked lists */
free_memory(nagios_macros * mac)4252 void free_memory(nagios_macros *mac) {
4253 	timed_event *this_event = NULL;
4254 	timed_event *next_event = NULL;
4255 
4256 	/* free all allocated memory for the object definitions */
4257 	free_object_data();
4258 
4259 	/* free memory allocated to comments */
4260 	free_comment_data();
4261 
4262 	/* free check result list */
4263 	free_check_result_list(&check_result_list);
4264 
4265 	/* free memory for the high priority event list */
4266 	this_event = event_list_high;
4267 	while(this_event != NULL) {
4268 		if(this_event->event_type == EVENT_SCHEDULED_DOWNTIME)
4269 			my_free(this_event->event_data);
4270 		next_event = this_event->next;
4271 		my_free(this_event);
4272 		this_event = next_event;
4273 		}
4274 
4275 	/* reset the event pointer */
4276 	event_list_high = NULL;
4277 
4278 	/* free memory for the low priority event list */
4279 	this_event = event_list_low;
4280 	while(this_event != NULL) {
4281 		if(this_event->event_type == EVENT_SCHEDULED_DOWNTIME)
4282 			my_free(this_event->event_data);
4283 		next_event = this_event->next;
4284 		my_free(this_event);
4285 		this_event = next_event;
4286 		}
4287 
4288 	/* reset the event pointer */
4289 	event_list_low = NULL;
4290 
4291 	/* free memory for global event handlers */
4292 	my_free(global_host_event_handler);
4293 	my_free(global_service_event_handler);
4294 
4295 	/* free any notification list that may have been overlooked */
4296 	free_notification_list();
4297 
4298 	/* free obsessive compulsive commands */
4299 	my_free(ocsp_command);
4300 	my_free(ochp_command);
4301 
4302 	/*
4303 	 * free memory associated with macros.
4304 	 * It's ok to only free the volatile ones, as the non-volatile
4305 	 * are always free()'d before assignment if they're set.
4306 	 * Doing a full free of them here means we'll wipe the constant
4307 	 * macros when we get a reload or restart request through the
4308 	 * command pipe, or when we receive a SIGHUP.
4309 	 */
4310 	clear_volatile_macros_r(mac);
4311 
4312 	free_macrox_names();
4313 
4314 	/* free illegal char strings */
4315 	my_free(illegal_object_chars);
4316 	my_free(illegal_output_chars);
4317 
4318 	/* free nagios user and group */
4319 	my_free(nagios_user);
4320 	my_free(nagios_group);
4321 
4322 	/* free version strings */
4323 	my_free(last_program_version);
4324 	my_free(new_program_version);
4325 
4326 	/* free file/path variables */
4327 	my_free(log_file);
4328 	my_free(debug_file);
4329 	my_free(temp_file);
4330 	my_free(temp_path);
4331 	my_free(check_result_path);
4332 	my_free(command_file);
4333 	my_free(lock_file);
4334 	my_free(auth_file);
4335 	my_free(p1_file);
4336 	my_free(log_archive_path);
4337 
4338 	return;
4339 	}
4340 
4341 
4342 /* free a notification list that was created */
free_notification_list(void)4343 void free_notification_list(void) {
4344 	notification *temp_notification = NULL;
4345 	notification *next_notification = NULL;
4346 
4347 	temp_notification = notification_list;
4348 	while(temp_notification != NULL) {
4349 		next_notification = temp_notification->next;
4350 		my_free(temp_notification);
4351 		temp_notification = next_notification;
4352 		}
4353 
4354 	/* reset notification list pointer */
4355 	notification_list = NULL;
4356 
4357 	return;
4358 	}
4359 
4360 
4361 /* reset all system-wide variables, so when we've receive a SIGHUP we can restart cleanly */
reset_variables(void)4362 int reset_variables(void) {
4363 
4364 	log_file = (char *)strdup(DEFAULT_LOG_FILE);
4365 	temp_file = (char *)strdup(DEFAULT_TEMP_FILE);
4366 	temp_path = (char *)strdup(DEFAULT_TEMP_PATH);
4367 	check_result_path = (char *)strdup(DEFAULT_CHECK_RESULT_PATH);
4368 	command_file = (char *)strdup(DEFAULT_COMMAND_FILE);
4369 	lock_file = (char *)strdup(DEFAULT_LOCK_FILE);
4370 	auth_file = (char *)strdup(DEFAULT_AUTH_FILE);
4371 	p1_file = (char *)strdup(DEFAULT_P1_FILE);
4372 	log_archive_path = (char *)strdup(DEFAULT_LOG_ARCHIVE_PATH);
4373 	debug_file = (char *)strdup(DEFAULT_DEBUG_FILE);
4374 
4375 	nagios_user = (char *)strdup(DEFAULT_NAGIOS_USER);
4376 	nagios_group = (char *)strdup(DEFAULT_NAGIOS_GROUP);
4377 
4378 	use_regexp_matches = FALSE;
4379 	use_true_regexp_matching = FALSE;
4380 
4381 	use_syslog = DEFAULT_USE_SYSLOG;
4382 	log_service_retries = DEFAULT_LOG_SERVICE_RETRIES;
4383 	log_host_retries = DEFAULT_LOG_HOST_RETRIES;
4384 	log_initial_states = DEFAULT_LOG_INITIAL_STATES;
4385 
4386 	log_notifications = DEFAULT_NOTIFICATION_LOGGING;
4387 	log_event_handlers = DEFAULT_LOG_EVENT_HANDLERS;
4388 	log_external_commands = DEFAULT_LOG_EXTERNAL_COMMANDS;
4389 	log_passive_checks = DEFAULT_LOG_PASSIVE_CHECKS;
4390 
4391 	logging_options = NSLOG_RUNTIME_ERROR | NSLOG_RUNTIME_WARNING | NSLOG_VERIFICATION_ERROR | NSLOG_VERIFICATION_WARNING | NSLOG_CONFIG_ERROR | NSLOG_CONFIG_WARNING | NSLOG_PROCESS_INFO | NSLOG_HOST_NOTIFICATION | NSLOG_SERVICE_NOTIFICATION | NSLOG_EVENT_HANDLER | NSLOG_EXTERNAL_COMMAND | NSLOG_PASSIVE_CHECK | NSLOG_HOST_UP | NSLOG_HOST_DOWN | NSLOG_HOST_UNREACHABLE | NSLOG_SERVICE_OK | NSLOG_SERVICE_WARNING | NSLOG_SERVICE_UNKNOWN | NSLOG_SERVICE_CRITICAL | NSLOG_INFO_MESSAGE;
4392 
4393 	syslog_options = NSLOG_RUNTIME_ERROR | NSLOG_RUNTIME_WARNING | NSLOG_VERIFICATION_ERROR | NSLOG_VERIFICATION_WARNING | NSLOG_CONFIG_ERROR | NSLOG_CONFIG_WARNING | NSLOG_PROCESS_INFO | NSLOG_HOST_NOTIFICATION | NSLOG_SERVICE_NOTIFICATION | NSLOG_EVENT_HANDLER | NSLOG_EXTERNAL_COMMAND | NSLOG_PASSIVE_CHECK | NSLOG_HOST_UP | NSLOG_HOST_DOWN | NSLOG_HOST_UNREACHABLE | NSLOG_SERVICE_OK | NSLOG_SERVICE_WARNING | NSLOG_SERVICE_UNKNOWN | NSLOG_SERVICE_CRITICAL | NSLOG_INFO_MESSAGE;
4394 
4395 	service_check_timeout = DEFAULT_SERVICE_CHECK_TIMEOUT;
4396 	host_check_timeout = DEFAULT_HOST_CHECK_TIMEOUT;
4397 	event_handler_timeout = DEFAULT_EVENT_HANDLER_TIMEOUT;
4398 	notification_timeout = DEFAULT_NOTIFICATION_TIMEOUT;
4399 	ocsp_timeout = DEFAULT_OCSP_TIMEOUT;
4400 	ochp_timeout = DEFAULT_OCHP_TIMEOUT;
4401 
4402 	sleep_time = DEFAULT_SLEEP_TIME;
4403 	interval_length = DEFAULT_INTERVAL_LENGTH;
4404 	service_inter_check_delay_method = ICD_SMART;
4405 	host_inter_check_delay_method = ICD_SMART;
4406 	service_interleave_factor_method = ILF_SMART;
4407 	max_service_check_spread = DEFAULT_SERVICE_CHECK_SPREAD;
4408 	max_host_check_spread = DEFAULT_HOST_CHECK_SPREAD;
4409 
4410 	use_aggressive_host_checking = DEFAULT_AGGRESSIVE_HOST_CHECKING;
4411 	cached_host_check_horizon = DEFAULT_CACHED_HOST_CHECK_HORIZON;
4412 	cached_service_check_horizon = DEFAULT_CACHED_SERVICE_CHECK_HORIZON;
4413 	enable_predictive_host_dependency_checks = DEFAULT_ENABLE_PREDICTIVE_HOST_DEPENDENCY_CHECKS;
4414 	enable_predictive_service_dependency_checks = DEFAULT_ENABLE_PREDICTIVE_SERVICE_DEPENDENCY_CHECKS;
4415 
4416 	soft_state_dependencies = FALSE;
4417 
4418 	retain_state_information = FALSE;
4419 	retention_update_interval = DEFAULT_RETENTION_UPDATE_INTERVAL;
4420 	use_retained_program_state = TRUE;
4421 	use_retained_scheduling_info = FALSE;
4422 	retention_scheduling_horizon = DEFAULT_RETENTION_SCHEDULING_HORIZON;
4423 	modified_host_process_attributes = MODATTR_NONE;
4424 	modified_service_process_attributes = MODATTR_NONE;
4425 	retained_host_attribute_mask = 0L;
4426 	retained_service_attribute_mask = 0L;
4427 	retained_process_host_attribute_mask = 0L;
4428 	retained_process_service_attribute_mask = 0L;
4429 	retained_contact_host_attribute_mask = 0L;
4430 	retained_contact_service_attribute_mask = 0L;
4431 
4432 	command_check_interval = DEFAULT_COMMAND_CHECK_INTERVAL;
4433 	check_reaper_interval = DEFAULT_CHECK_REAPER_INTERVAL;
4434 	max_check_reaper_time = DEFAULT_MAX_REAPER_TIME;
4435 	max_check_result_file_age = DEFAULT_MAX_CHECK_RESULT_AGE;
4436 	service_freshness_check_interval = DEFAULT_FRESHNESS_CHECK_INTERVAL;
4437 	host_freshness_check_interval = DEFAULT_FRESHNESS_CHECK_INTERVAL;
4438 	auto_rescheduling_interval = DEFAULT_AUTO_RESCHEDULING_INTERVAL;
4439 	auto_rescheduling_window = DEFAULT_AUTO_RESCHEDULING_WINDOW;
4440 
4441 	check_external_commands = DEFAULT_CHECK_EXTERNAL_COMMANDS;
4442 	check_orphaned_services = DEFAULT_CHECK_ORPHANED_SERVICES;
4443 	check_orphaned_hosts = DEFAULT_CHECK_ORPHANED_HOSTS;
4444 	check_service_freshness = DEFAULT_CHECK_SERVICE_FRESHNESS;
4445 	check_host_freshness = DEFAULT_CHECK_HOST_FRESHNESS;
4446 	auto_reschedule_checks = DEFAULT_AUTO_RESCHEDULE_CHECKS;
4447 
4448 	log_rotation_method = LOG_ROTATION_NONE;
4449 
4450 	last_command_check = 0L;
4451 	last_command_status_update = 0L;
4452 	last_log_rotation = 0L;
4453 
4454 	max_parallel_service_checks = DEFAULT_MAX_PARALLEL_SERVICE_CHECKS;
4455 	currently_running_service_checks = 0;
4456 
4457 	enable_notifications = TRUE;
4458 	execute_service_checks = TRUE;
4459 	accept_passive_service_checks = TRUE;
4460 	execute_host_checks = TRUE;
4461 	accept_passive_service_checks = TRUE;
4462 	enable_event_handlers = TRUE;
4463 	obsess_over_services = FALSE;
4464 	obsess_over_hosts = FALSE;
4465 	enable_failure_prediction = TRUE;
4466 
4467 	next_comment_id = 0L; /* comment and downtime id get initialized to nonzero elsewhere */
4468 	next_downtime_id = 0L;
4469 	next_event_id = 1;
4470 	next_notification_id = 1;
4471 
4472 	aggregate_status_updates = TRUE;
4473 	status_update_interval = DEFAULT_STATUS_UPDATE_INTERVAL;
4474 
4475 	event_broker_options = BROKER_NOTHING;
4476 
4477 	time_change_threshold = DEFAULT_TIME_CHANGE_THRESHOLD;
4478 
4479 	enable_flap_detection = DEFAULT_ENABLE_FLAP_DETECTION;
4480 	low_service_flap_threshold = DEFAULT_LOW_SERVICE_FLAP_THRESHOLD;
4481 	high_service_flap_threshold = DEFAULT_HIGH_SERVICE_FLAP_THRESHOLD;
4482 	low_host_flap_threshold = DEFAULT_LOW_HOST_FLAP_THRESHOLD;
4483 	high_host_flap_threshold = DEFAULT_HIGH_HOST_FLAP_THRESHOLD;
4484 
4485 	process_performance_data = DEFAULT_PROCESS_PERFORMANCE_DATA;
4486 
4487 	translate_passive_host_checks = DEFAULT_TRANSLATE_PASSIVE_HOST_CHECKS;
4488 	passive_host_checks_are_soft = DEFAULT_PASSIVE_HOST_CHECKS_SOFT;
4489 
4490 	use_large_installation_tweaks = DEFAULT_USE_LARGE_INSTALLATION_TWEAKS;
4491 	enable_environment_macros = FALSE;
4492 	free_child_process_memory = FALSE;
4493 	child_processes_fork_twice = FALSE;
4494 
4495 	additional_freshness_latency = DEFAULT_ADDITIONAL_FRESHNESS_LATENCY;
4496 
4497 	enable_embedded_perl = DEFAULT_ENABLE_EMBEDDED_PERL;
4498 	use_embedded_perl_implicitly = DEFAULT_USE_EMBEDDED_PERL_IMPLICITLY;
4499 
4500 	external_command_buffer_slots = DEFAULT_EXTERNAL_COMMAND_BUFFER_SLOTS;
4501 
4502 	debug_level = DEFAULT_DEBUG_LEVEL;
4503 	debug_verbosity = DEFAULT_DEBUG_VERBOSITY;
4504 	max_debug_file_size = DEFAULT_MAX_DEBUG_FILE_SIZE;
4505 
4506 	date_format = DATE_FORMAT_US;
4507 
4508 	/* initialize macros */
4509 	init_macros();
4510 
4511 	global_host_event_handler = NULL;
4512 	global_service_event_handler = NULL;
4513 	global_host_event_handler_ptr = NULL;
4514 	global_service_event_handler_ptr = NULL;
4515 
4516 	ocsp_command = NULL;
4517 	ochp_command = NULL;
4518 	ocsp_command_ptr = NULL;
4519 	ochp_command_ptr = NULL;
4520 
4521 	/* reset umask */
4522 	umask(S_IWGRP | S_IWOTH);
4523 
4524 	return OK;
4525 	}
4526 
4527