/* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Healthcheckrs child process handling. * * Author: Alexandre Cassen, * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Copyright (C) 2001-2017 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include #include #ifdef THREAD_DUMP #ifdef _WITH_SNMP_ #include "snmp.h" #endif #include "scheduler.h" #include "smtp.h" #include "check_dns.h" #include "check_http.h" #include "check_misc.h" #include "check_smtp.h" #include "check_tcp.h" #include "check_udp.h" #endif #include "check_daemon.h" #include "check_parser.h" #include "ipwrapper.h" #include "check_ssl.h" #include "check_api.h" #include "check_ping.h" #include "check_file.h" #include "global_data.h" #include "pidfile.h" #include "signals.h" #include "process.h" #include "memory.h" #include "logger.h" #include "main.h" #include "parser.h" #include "bitops.h" #include "keepalived_netlink.h" #include "check_print.h" #ifdef _WITH_SNMP_CHECKER_ #include "check_snmp.h" #endif #include "utils.h" #ifdef _WITH_BFD_ #include "bfd_daemon.h" #include "check_bfd.h" #endif #include "timer.h" #include "track_file.h" #ifdef _WITH_CN_PROC_ #include "track_process.h" #endif /* Global variables */ bool using_ha_suspend; /* local variables */ static const char *check_syslog_ident; #ifndef __ONE_PROCESS_DEBUG_ static bool two_phase_terminate; static timeval_t check_start_time; static unsigned check_next_restart_delay; #endif /* set fd ulimits */ static void set_checker_max_fds(void) { /* Allow for: * 0 stdin * 1 stdout * 2 strerr * 3 memcheck log (debugging) * 4 log file * 5 epoll * 6 timerfd * 7 signalfd * 8 bfd pipe * 9 closed * 10 closed * 11 FIFO * 12 closed * 13 passwd file * 14 Unix domain socket * One per checker using UDP/TCP/PING * One per SMTP alert * qty 10 spare */ set_max_file_limit(14 + check_data->num_checker_fd_required + check_data->num_smtp_alert + 10); } static void lvs_notify_fifo_script_exit(__attribute__((unused)) thread_ref_t thread) { log_message(LOG_INFO, "lvs notify fifo script terminated"); } static void checker_dispatcher_release(void) { #ifdef _WITH_BFD_ checker_bfd_dispatcher_release(); #endif cancel_signal_read_thread(); cancel_kernel_netlink_threads(); } static bool checker_ipvs_syncd_needed(void) { #ifdef _WITH_VRRP_ if (global_data->lvs_syncd.vrrp_name) return false; #endif return !!global_data->lvs_syncd.ifname; } /* Daemon stop sequence */ static int checker_terminate_phase2(void) { struct rusage usage; /* Remove the notify fifo */ notify_fifo_close(&global_data->notify_fifo, &global_data->lvs_notify_fifo); #ifdef _WITH_SNMP_CHECKER_ if (global_data && global_data->enable_snmp_checker) check_snmp_agent_close(); #endif /* Destroy master thread */ checker_dispatcher_release(); thread_destroy_master(master); master = NULL; free_checkers_queue(); free_ssl(); set_ping_group_range(false); /* If we are running both master and backup, stop them now */ if (checker_ipvs_syncd_needed()) { ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_MASTER, true); ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_BACKUP, true); } ipvs_stop(); /* Stop daemon */ pidfile_rm(checkers_pidfile); /* Clean data */ if (global_data) free_global_data(global_data); if (check_data) free_check_data(check_data); free_parent_mallocs_exit(); /* * Reached when terminate signal catched. * finally return to parent process. */ if (__test_bit(LOG_DETAIL_BIT, &debug)) { getrusage(RUSAGE_SELF, &usage); log_message(LOG_INFO, "Stopped - used %ld.%6.6ld user time, %ld.%6.6ld system time", usage.ru_utime.tv_sec, usage.ru_utime.tv_usec, usage.ru_stime.tv_sec, usage.ru_stime.tv_usec); } else log_message(LOG_INFO, "Stopped"); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) close_log_file(); #endif closelog(); #ifndef _MEM_CHECK_LOG_ FREE_CONST_PTR(check_syslog_ident); #else if (check_syslog_ident) free(no_const_char_p(check_syslog_ident)); /* malloc'd by make_syslog_ident() */ #endif close_std_fd(); return 0; } static void checker_shutdown_backstop_thread(thread_ref_t thread) { int count = 0; thread_ref_t t; /* Force terminate all script processes */ if (thread->master->child.rb_root.rb_node) script_killall(thread->master, SIGKILL, true); rb_for_each_entry_cached_const(t, &thread->master->child, n) count++; log_message(LOG_ERR, "backstop thread invoked: shutdown timer %srunning, child count %d", thread->master->shutdown_timer_running ? "" : "not ", count); if (thread->master->shutdown_timer_running) thread_add_timer_shutdown(thread->master, checker_shutdown_backstop_thread, NULL, TIMER_HZ / 10); else thread_add_terminate_event(thread->master); } static void checker_terminate_phase1(bool schedule_next_thread) { if (using_ha_suspend || __test_bit(LOG_ADDRESS_CHANGES, &debug)) kernel_netlink_close(); /* Terminate all script processes */ if (master->child.rb_root.rb_node) script_killall(master, SIGTERM, true); /* Stop monitoring files */ if (!list_empty(&check_data->track_files)) stop_track_files(); /* Send shutdown messages */ if (!__test_bit(DONT_RELEASE_IPVS_BIT, &debug)) { if (global_data->lvs_flush_onstop == LVS_FLUSH_FULL) { log_message(LOG_INFO, "Flushing lvs on shutdown in oneshot"); ipvs_flush_cmd(); } else clear_services(); } if (schedule_next_thread) { /* If there are no child processes, we can terminate immediately, * otherwise add a thread to allow reasonable time for children to terminate */ if (master->child.rb_root.rb_node) { /* Add a backstop timer for the shutdown */ thread_add_timer_shutdown(master, checker_shutdown_backstop_thread, NULL, TIMER_HZ); } else thread_add_terminate_event(master); } } #ifndef _ONE_PROCESS_DEBUG_ static void start_checker_termination_thread(__attribute__((unused)) thread_ref_t thread) { /* This runs in the context of a thread */ two_phase_terminate = true; checker_terminate_phase1(true); } #endif /* Daemon stop sequence */ static void stop_check(int status) { if (__test_bit(CONFIG_TEST_BIT, &debug)) return; /* This runs in the main process, not in the context of a thread */ checker_terminate_phase1(false); checker_terminate_phase2(); /* unreachable */ exit(status); } /* Daemon init sequence */ static void start_check(list_head_t *old_checkers_queue, data_t *prev_global_data) { init_checkers_queue(); /* Parse configuration file */ if (reload) global_data = alloc_global_data(); check_data = alloc_check_data(); if (!check_data) { stop_check(KEEPALIVED_EXIT_FATAL); return; } init_data(conf_file, check_init_keywords); if (reload) init_global_data(global_data, prev_global_data, true); /* Update process name if necessary */ if ((!reload && global_data->lvs_process_name) || (reload && (!global_data->lvs_process_name != !prev_global_data->lvs_process_name || (global_data->lvs_process_name && strcmp(global_data->lvs_process_name, prev_global_data->lvs_process_name))))) set_process_name(global_data->lvs_process_name); /* fill 'vsg' members of the virtual_server_t structure. * We must do that after parsing config, because * vs and vsg declarations may appear in any order, * but we must do it before validate_check_config(). */ link_vsg_to_vs(); /* Post initializations */ if (!validate_check_config()) { stop_check(KEEPALIVED_EXIT_CONFIG); return; } #ifdef _MEM_CHECK_ log_message(LOG_INFO, "Configuration is using : %zu Bytes", mem_allocated); #endif /* If we are just testing the configuration, then we terminate now */ if (__test_bit(CONFIG_TEST_BIT, &debug)) return; /* Initialize sub-system if any virtual servers are configured */ if ((!list_empty(&check_data->vs) || (reload && !list_empty(&old_check_data->vs))) && ipvs_start() != IPVS_SUCCESS) { stop_check(KEEPALIVED_EXIT_FATAL); return; } /* Set LVS timeouts */ if (global_data->lvs_timeouts.tcp_timeout || global_data->lvs_timeouts.tcp_fin_timeout || global_data->lvs_timeouts.udp_timeout) ipvs_set_timeouts(&global_data->lvs_timeouts); else if (reload) ipvs_set_timeouts(NULL); /* If we are managing the sync daemon, then stop any * instances of it that may have been running if * we terminated abnormally */ if (checker_ipvs_syncd_needed() && (!reload || ipvs_syncd_changed(&prev_global_data->lvs_syncd, &global_data->lvs_syncd))) { ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_MASTER, true); ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_BACKUP, true); } if (checker_ipvs_syncd_needed()) { /* If we are running both master and backup, start them now */ if (global_data->lvs_syncd.syncid == PARAMETER_UNSET) global_data->lvs_syncd.syncid = 0; ipvs_syncd_cmd(IPVS_STARTDAEMON, &global_data->lvs_syncd, IPVS_MASTER_BACKUP, false); } /* Ensure we can open sufficient file descriptors */ set_checker_max_fds(); /* Create a notify FIFO if needed, and open it */ notify_fifo_open(&global_data->notify_fifo, &global_data->lvs_notify_fifo, lvs_notify_fifo_script_exit, "lvs_"); /* Get current active addresses, and start update process */ if (using_ha_suspend || __test_bit(LOG_ADDRESS_CHANGES, &debug)) { if (reload) kernel_netlink_set_recv_bufs(); kernel_netlink_init(); } else if (reload) kernel_netlink_close(); /* Remove any entries left over from previous invocation */ if (!reload && global_data->lvs_flush) ipvs_flush_cmd(); #ifdef _WITH_SNMP_CHECKER_ if (global_data->enable_snmp_checker) { if (reload) snmp_epoll_info(master); else check_snmp_agent_init(global_data->snmp_socket); } #endif /* SSL load static data & initialize common ctx context */ if (check_data->ssl_required && !init_ssl_ctx()) stop_check(KEEPALIVED_EXIT_FATAL); /* We can send SMTP messages from here so set the time */ set_time_now(); /* Set up the track files */ add_rs_to_track_files(); init_track_files(&check_data->track_files); /* Processing differential configuration parsing */ if (reload) clear_diff_services(old_checkers_queue); set_track_file_checkers_down(); if (reload) check_new_rs_state(); /* Initialize IPVS topology */ if (!init_services()) stop_check(KEEPALIVED_EXIT_FATAL); /* Dump configuration */ if (__test_bit(DUMP_CONF_BIT, &debug)) dump_data_check(NULL); /* Register checkers thread */ register_checkers_thread(); /* Set the process priority and non swappable if configured */ set_process_priorities(global_data->checker_realtime_priority, global_data->max_auto_priority, global_data->min_auto_priority_delay, #if HAVE_DECL_RLIMIT_RTTIME == 1 global_data->checker_rlimit_rt, #endif global_data->checker_process_priority, global_data->checker_no_swap ? 4096 : 0); /* Set the process cpu affinity if configured */ set_process_cpu_affinity(&global_data->checker_cpu_mask, "checker"); } void check_validate_config(void) { start_check(NULL, NULL); } #ifndef _ONE_PROCESS_DEBUG_ /* Reload thread */ static void reload_check_thread(__attribute__((unused)) thread_ref_t thread) { list_head_t old_checkers_queue; bool with_snmp = false; log_message(LOG_INFO, "Reloading"); /* Use standard scheduling while reloading */ reset_process_priorities(); /* set the reloading flag */ SET_RELOAD; log_message(LOG_INFO, "Got SIGHUP, reloading checker configuration"); /* Terminate all script process */ script_killall(master, SIGTERM, false); if (!list_empty(&check_data->track_files)) stop_track_files(); /* Remove the notify fifo - we don't know if it will be the same after a reload */ notify_fifo_close(&global_data->notify_fifo, &global_data->lvs_notify_fifo); #if !defined _ONE_PROCESS_DEBUG_ && defined _WITH_SNMP_CHECKER_ if (prog_type == PROG_TYPE_CHECKER && global_data->enable_snmp_checker) with_snmp = true; #endif /* Destroy master thread */ checker_dispatcher_release(); thread_cleanup_master(master); thread_add_base_threads(master, with_snmp); /* Save previous checker data */ list_copy(&old_checkers_queue, &checkers_queue); init_checkers_queue(); free_ssl(); ipvs_stop(); /* Save previous conf data */ old_check_data = check_data; check_data = NULL; old_global_data = global_data; global_data = NULL; /* Reload the conf */ start_check(&old_checkers_queue, old_global_data); /* free backup data */ free_check_data(old_check_data); free_global_data(old_global_data); free_checker_list(&old_checkers_queue); UNSET_RELOAD; } static void print_check_data(__attribute__((unused)) thread_ref_t thread) { check_print_data(); } static void sigusr1_check(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { log_message(LOG_INFO, "Printing checker data for process(%d) on signal", getpid()); thread_add_event(master, print_check_data, NULL, 0); } static void sigreload_check(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { thread_add_event(master, reload_check_thread, NULL, 0); } /* Terminate handler */ static void sigend_check(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { if (master) thread_add_start_terminate_event(master, start_checker_termination_thread); } /* CHECK Child signal handling */ static void check_signal_init(void) { signal_set(SIGHUP, sigreload_check, NULL); signal_set(SIGINT, sigend_check, NULL); signal_set(SIGTERM, sigend_check, NULL); signal_set(SIGUSR1, sigusr1_check, NULL); #ifdef THREAD_DUMP signal_set(SIGTDUMP, thread_dump_signal, NULL); #endif signal_ignore(SIGPIPE); } /* This function runs in the parent process. */ static void delayed_restart_check_child_thread(__attribute__((unused)) thread_ref_t thread) { start_check_child(); } /* CHECK Child respawning thread. This function runs in the parent process. */ static void check_respawn_thread(thread_ref_t thread) { unsigned restart_delay; /* We catch a SIGCHLD, handle it */ checkers_child = 0; if (report_child_status(thread->u.c.status, thread->u.c.pid, NULL)) thread_add_terminate_event(thread->master); else if (!__test_bit(DONT_RESPAWN_BIT, &debug)) { log_message(LOG_ALERT, "Healthcheck child process(%d) died: Respawning", thread->u.c.pid); restart_delay = calc_restart_delay(&check_start_time, &check_next_restart_delay, "Healthcheck"); if (!restart_delay) start_check_child(); else thread_add_timer(thread->master, delayed_restart_check_child_thread, NULL, restart_delay * TIMER_HZ); } else { log_message(LOG_ALERT, "Healthcheck child process(%d) died: Exiting", thread->u.c.pid); raise(SIGTERM); } } #endif #ifdef THREAD_DUMP static void register_check_thread_addresses(void) { /* Remove anything we might have inherited from parent */ deregister_thread_addresses(); register_scheduler_addresses(); register_signal_thread_addresses(); register_notify_addresses(); register_smtp_addresses(); register_keepalived_netlink_addresses(); #ifdef _WITH_SNMP_ register_snmp_addresses(); #endif register_check_dns_addresses(); register_check_http_addresses(); register_check_misc_addresses(); register_check_smtp_addresses(); register_check_ssl_addresses(); register_check_tcp_addresses(); register_check_ping_addresses(); register_check_udp_addresses(); register_check_file_addresses(); #ifdef _WITH_BFD_ register_check_bfd_addresses(); #endif #ifndef _ONE_PROCESS_DEBUG_ register_thread_address("reload_check_thread", reload_check_thread); register_thread_address("start_checker_termination_thread", start_checker_termination_thread); #endif register_thread_address("lvs_notify_fifo_script_exit", lvs_notify_fifo_script_exit); register_thread_address("checker_shutdown_backstop_thread", checker_shutdown_backstop_thread); #ifndef _ONE_PROCESS_DEBUG_ register_signal_handler_address("sigreload_check", sigreload_check); register_signal_handler_address("sigend_check", sigend_check); #endif } #endif /* Register CHECK thread */ int start_check_child(void) { #ifndef _ONE_PROCESS_DEBUG_ pid_t pid; const char *syslog_ident; /* Initialize child process */ #ifdef ENABLE_LOG_TO_FILE if (log_file_name) flush_log_file(); #endif pid = fork(); if (pid < 0) { log_message(LOG_INFO, "Healthcheck child process: fork error(%s)" , strerror(errno)); return -1; } else if (pid) { checkers_child = pid; check_start_time = time_now; log_message(LOG_INFO, "Starting Healthcheck child process, pid=%d" , pid); /* Start respawning thread */ thread_add_child(master, check_respawn_thread, NULL, pid, TIMER_NEVER); return 0; } prctl(PR_SET_PDEATHSIG, SIGTERM); prog_type = PROG_TYPE_CHECKER; initialise_debug_options(); #ifdef _WITH_BFD_ /* Close the write end of the BFD checker event notification pipe and the track_process fd */ close(bfd_checker_event_pipe[1]); #ifdef _WITH_VRRP_ close(bfd_vrrp_event_pipe[0]); close(bfd_vrrp_event_pipe[1]); #endif #endif #ifdef _WITH_CN_PROC_ close_track_processes(); #endif if ((global_data->instance_name #if HAVE_DECL_CLONE_NEWNET || global_data->network_namespace #endif ) && (check_syslog_ident = make_syslog_ident(PROG_CHECK))) syslog_ident = check_syslog_ident; else syslog_ident = PROG_CHECK; /* Opening local CHECK syslog channel */ if (!__test_bit(NO_SYSLOG_BIT, &debug)) openlog(syslog_ident, LOG_PID | ((__test_bit(LOG_CONSOLE_BIT, &debug)) ? LOG_CONS : 0) , (log_facility==LOG_DAEMON) ? LOG_LOCAL2 : log_facility); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) open_log_file(log_file_name, "check", #if HAVE_DECL_CLONE_NEWNET global_data->network_namespace, #else NULL, #endif global_data->instance_name); #endif #ifdef _MEM_CHECK_ mem_log_init(PROG_CHECK, "Healthcheck child process"); #endif free_parent_mallocs_startup(true); /* Clear any child finder functions set in parent */ set_child_finder_name(NULL); /* Child process part, write pidfile */ if (!pidfile_write(checkers_pidfile, getpid())) { log_message(LOG_INFO, "Healthcheck child process: cannot write pidfile"); exit(KEEPALIVED_EXIT_FATAL); } /* Create the new master thread */ thread_destroy_master(master); /* This destroys any residual settings from the parent */ master = thread_make_master(); #endif /* If last process died during a reload, we can get there and we * don't want to loop again, because we're not reloading anymore. */ UNSET_RELOAD; #ifndef _ONE_PROCESS_DEBUG_ /* Signal handling initialization */ check_signal_init(); #endif /* Start Healthcheck daemon */ start_check(NULL, NULL); #ifdef _ONE_PROCESS_DEBUG_ return 0; #endif #ifdef THREAD_DUMP register_check_thread_addresses(); #endif /* Launch the scheduling I/O multiplexer */ launch_thread_scheduler(master); /* Finish healthchecker daemon process */ if (two_phase_terminate) checker_terminate_phase2(); else stop_check(KEEPALIVED_EXIT_OK); #ifdef THREAD_DUMP deregister_thread_addresses(); #endif /* unreachable */ exit(KEEPALIVED_EXIT_OK); } #ifdef THREAD_DUMP void register_check_parent_addresses(void) { #ifndef _ONE_PROCESS_DEBUG_ register_thread_address("print_check_data", print_check_data); register_thread_address("check_respawn_thread", check_respawn_thread); register_thread_address("delayed_restart_check_child_thread", delayed_restart_check_child_thread); #endif } #endif