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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_time);
1733 t = localtime_r(¤t_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(¤t_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(¤t_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(×tamp);
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(¤t_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(¤t_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(¤t_time);
3980
3981 /*
3982 printf("NOW: %s",ctime(¤t_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