diff --git a/keepalived/bfd/bfd_daemon.c b/keepalived/bfd/bfd_daemon.c index 4d20f159e..5f908662d 100644 --- a/keepalived/bfd/bfd_daemon.c +++ b/keepalived/bfd/bfd_daemon.c @@ -82,7 +82,7 @@ stop_bfd(int status) return; /* Stop daemon */ - pidfile_rm(bfd_pidfile); + pidfile_rm(&bfd_pidfile); /* Clean data */ free_global_data(global_data); @@ -407,6 +407,8 @@ start_bfd_child(void) prog_type = PROG_TYPE_BFD; + close_other_pidfiles(); + /* Close the read end of the event notification pipes, and the track_process fd */ #ifdef _WITH_VRRP_ close(bfd_vrrp_event_pipe[0]); @@ -456,7 +458,7 @@ start_bfd_child(void) separate_config_file(); /* Child process part, write pidfile */ - if (!pidfile_write(bfd_pidfile, getpid())) { + if (!pidfile_write(&bfd_pidfile)) { /* Fatal error */ log_message(LOG_INFO, "BFD child process: cannot write pidfile"); diff --git a/keepalived/check/check_daemon.c b/keepalived/check/check_daemon.c index 887b00d5f..d98411e18 100644 --- a/keepalived/check/check_daemon.c +++ b/keepalived/check/check_daemon.c @@ -177,7 +177,7 @@ checker_terminate_phase2(void) ipvs_stop(); /* Stop daemon */ - pidfile_rm(checkers_pidfile); + pidfile_rm(&checkers_pidfile); /* Clean data */ if (global_data) @@ -710,6 +710,8 @@ start_check_child(void) deregister_thread_addresses(); #endif + close_other_pidfiles(); + #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]); @@ -758,7 +760,7 @@ start_check_child(void) separate_config_file(); /* Child process part, write pidfile */ - if (!pidfile_write(checkers_pidfile, getpid())) { + if (!pidfile_write(&checkers_pidfile)) { log_message(LOG_INFO, "Healthcheck child process: cannot write pidfile"); exit(KEEPALIVED_EXIT_FATAL); } diff --git a/keepalived/core/main.c b/keepalived/core/main.c index 6ef34ebe2..703b89b42 100644 --- a/keepalived/core/main.c +++ b/keepalived/core/main.c @@ -149,22 +149,18 @@ static const struct child_term children_term[] = { const char *version_string = VERSION_STRING; /* keepalived version */ const char *conf_file; /* Configuration file */ bool reload; /* Set during a reload */ -const char *main_pidfile; /* overrule default pidfile */ -static bool free_main_pidfile; +struct pidfile main_pidfile = { .fd = -1 }; /* overrule default pidfile */ #ifdef _WITH_LVS_ pid_t checkers_child; /* Healthcheckers child process ID */ -const char *checkers_pidfile; /* overrule default pidfile */ -static bool free_checkers_pidfile; +struct pidfile checkers_pidfile = { .fd = -1 }; /* overrule default pidfile */ #endif #ifdef _WITH_VRRP_ pid_t vrrp_child; /* VRRP child process ID */ -const char *vrrp_pidfile; /* overrule default pidfile */ -static bool free_vrrp_pidfile; +struct pidfile vrrp_pidfile = { .fd = -1 }; /* overrule default pidfile */ #endif #ifdef _WITH_BFD_ pid_t bfd_child; /* BFD child process ID */ -const char *bfd_pidfile; /* overrule default pidfile */ -static bool free_bfd_pidfile; +struct pidfile bfd_pidfile = { .fd = -1 }; /* overrule default pidfile */ #endif unsigned long daemon_mode; /* VRRP/CHECK/BFD subsystem selection */ #ifdef _WITH_SNMP_ @@ -319,29 +315,11 @@ free_parent_mallocs_startup(bool am_child) free_notify_script(&global_data->startup_script); free_notify_script(&global_data->shutdown_script); } - - if (free_main_pidfile) { - FREE_CONST_PTR(main_pidfile); - free_main_pidfile = false; - } } void free_parent_mallocs_exit(void) { -#ifdef _WITH_VRRP_ - if (free_vrrp_pidfile) - FREE_CONST_PTR(vrrp_pidfile); -#endif -#ifdef _WITH_LVS_ - if (free_checkers_pidfile) - FREE_CONST_PTR(checkers_pidfile); -#endif -#ifdef _WITH_BFD_ - if (free_bfd_pidfile) - FREE_CONST_PTR(bfd_pidfile); -#endif - FREE_CONST_PTR(config_id); #ifdef _REPRODUCIBLE_BUILD_ @@ -514,20 +492,20 @@ stop_keepalived(void) #ifdef _WITH_VRRP_ if (__test_bit(DAEMON_VRRP, &daemon_mode)) - pidfile_rm(vrrp_pidfile); + pidfile_close(&vrrp_pidfile, true); #endif #ifdef _WITH_LVS_ if (__test_bit(DAEMON_CHECKERS, &daemon_mode)) - pidfile_rm(checkers_pidfile); + pidfile_close(&checkers_pidfile, true); #endif #ifdef _WITH_BFD_ if (__test_bit(DAEMON_BFD, &daemon_mode)) - pidfile_rm(bfd_pidfile); + pidfile_close(&bfd_pidfile, true); #endif - pidfile_rm(main_pidfile); + pidfile_rm(&main_pidfile); #endif } @@ -560,7 +538,8 @@ start_keepalived(__attribute__((unused)) thread_ref_t thread) start_check_child(); have_child = true; num_reloading++; - } + } else + pidfile_rm(&checkers_pidfile); #endif #ifdef _WITH_VRRP_ /* start vrrp child */ @@ -568,7 +547,8 @@ start_keepalived(__attribute__((unused)) thread_ref_t thread) start_vrrp_child(); have_child = true; num_reloading++; - } + } else + pidfile_rm(&vrrp_pidfile); #endif #ifdef _WITH_BFD_ /* start bfd child */ @@ -576,7 +556,8 @@ start_keepalived(__attribute__((unused)) thread_ref_t thread) start_bfd_child(); have_child = true; num_reloading++; - } + } else + pidfile_rm(&bfd_pidfile); #endif #ifndef _ONE_PROCESS_DEBUG_ @@ -2255,11 +2236,11 @@ parse_cmdline(int argc, char **argv) break; #endif case 'p': - main_pidfile = optarg; + main_pidfile.path = optarg; break; #ifdef _WITH_LVS_ case 'c': - checkers_pidfile = optarg; + checkers_pidfile.path = optarg; break; case 'a': __set_bit(LOG_ADDRESS_CHANGES, &debug); @@ -2267,12 +2248,12 @@ parse_cmdline(int argc, char **argv) #endif #ifdef _WITH_VRRP_ case 'r': - vrrp_pidfile = optarg; + vrrp_pidfile.path = optarg; break; #endif #ifdef _WITH_BFD_ case 'b': - bfd_pidfile = optarg; + bfd_pidfile.path = optarg; break; #endif #ifdef _WITH_SNMP_ @@ -2733,53 +2714,53 @@ keepalived_main(int argc, char **argv) if (!__test_bit(CONFIG_TEST_BIT, &debug)) { if (global_data->instance_name) { - if (!main_pidfile && (main_pidfile = make_pidfile_name(KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE, global_data->instance_name, PID_EXTENSION))) - free_main_pidfile = true; + if (!main_pidfile.path && (main_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE, global_data->instance_name, PID_EXTENSION))) + main_pidfile.free_path = true; #ifdef _WITH_LVS_ - if (!checkers_pidfile && (checkers_pidfile = make_pidfile_name(KEEPALIVED_PID_DIR CHECKERS_PID_FILE, global_data->instance_name, PID_EXTENSION))) - free_checkers_pidfile = true; + if (!checkers_pidfile.path && (checkers_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR CHECKERS_PID_FILE, global_data->instance_name, PID_EXTENSION))) + checkers_pidfile.free_path = true; #endif #ifdef _WITH_VRRP_ - if (!vrrp_pidfile && (vrrp_pidfile = make_pidfile_name(KEEPALIVED_PID_DIR VRRP_PID_FILE, global_data->instance_name, PID_EXTENSION))) - free_vrrp_pidfile = true; + if (!vrrp_pidfile.path && (vrrp_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR VRRP_PID_FILE, global_data->instance_name, PID_EXTENSION))) + vrrp_pidfile.free_path = true; #endif #ifdef _WITH_BFD_ - if (!bfd_pidfile && (bfd_pidfile = make_pidfile_name(KEEPALIVED_PID_DIR BFD_PID_FILE, global_data->instance_name, PID_EXTENSION))) - free_bfd_pidfile = true; + if (!bfd_pidfile.path && (bfd_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR BFD_PID_FILE, global_data->instance_name, PID_EXTENSION))) + bfd_pidfile.free_path = true; #endif } if (use_pid_dir) { - if (!main_pidfile) - main_pidfile = KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE PID_EXTENSION; + if (!main_pidfile.path) + main_pidfile.path = KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE PID_EXTENSION; #ifdef _WITH_LVS_ - if (!checkers_pidfile) - checkers_pidfile = KEEPALIVED_PID_DIR CHECKERS_PID_FILE PID_EXTENSION; + if (!checkers_pidfile.path) + checkers_pidfile.path = KEEPALIVED_PID_DIR CHECKERS_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_VRRP_ - if (!vrrp_pidfile) - vrrp_pidfile = KEEPALIVED_PID_DIR VRRP_PID_FILE PID_EXTENSION; + if (!vrrp_pidfile.path) + vrrp_pidfile.path = KEEPALIVED_PID_DIR VRRP_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_BFD_ - if (!bfd_pidfile) - bfd_pidfile = KEEPALIVED_PID_DIR BFD_PID_FILE PID_EXTENSION; + if (!bfd_pidfile.path) + bfd_pidfile.path = KEEPALIVED_PID_DIR BFD_PID_FILE PID_EXTENSION; #endif } else { - if (!main_pidfile) - main_pidfile = RUNSTATEDIR "/" KEEPALIVED_PID_FILE PID_EXTENSION; + if (!main_pidfile.path) + main_pidfile.path = RUNSTATEDIR "/" KEEPALIVED_PID_FILE PID_EXTENSION; #ifdef _WITH_LVS_ - if (!checkers_pidfile) - checkers_pidfile = RUNSTATEDIR "/" CHECKERS_PID_FILE PID_EXTENSION; + if (!checkers_pidfile.path) + checkers_pidfile.path = RUNSTATEDIR "/" CHECKERS_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_VRRP_ - if (!vrrp_pidfile) - vrrp_pidfile = RUNSTATEDIR "/" VRRP_PID_FILE PID_EXTENSION; + if (!vrrp_pidfile.path) + vrrp_pidfile.path = RUNSTATEDIR "/" VRRP_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_BFD_ - if (!bfd_pidfile) - bfd_pidfile = RUNSTATEDIR "/" BFD_PID_FILE PID_EXTENSION; + if (!bfd_pidfile.path) + bfd_pidfile.path = RUNSTATEDIR "/" BFD_PID_FILE PID_EXTENSION; #endif } @@ -2849,7 +2830,7 @@ keepalived_main(int argc, char **argv) } /* write the father's pidfile */ - if (!pidfile_write(main_pidfile, getpid())) + if (!pidfile_write(&main_pidfile)) goto end; if (!global_data->max_auto_priority) diff --git a/keepalived/core/pidfile.c b/keepalived/core/pidfile.c index 911525458..cf49aee98 100644 --- a/keepalived/core/pidfile.c +++ b/keepalived/core/pidfile.c @@ -27,8 +27,9 @@ #include #include #include -#include #include +#include +#include #include #include "logger.h" @@ -103,74 +104,135 @@ make_pidfile_name(const char* start, const char* instance, const char* extn) return name; } -/* Create the running daemon pidfile */ -bool -pidfile_write(const char *pid_file, int pid) +void +pidfile_close(pidfile_t *pidf, bool free_path) { - FILE *pidfile = NULL; - int pidfd; + if (pidf->fd == -1) + return; - /* We want to create the file with permissions rx-r--r-- */ - if (umask_val & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) - umask(umask_val & ~(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); + close(pidf->fd); + pidf->fd = -1; - pidfd = open(pid_file, O_NOFOLLOW | O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (free_path && pidf->free_path) { + FREE_CONST_PTR(pidf->path); + pidf->path = NULL; + pidf->free_path = false; + } +} - /* Restore the default umask */ - if (umask_val & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) - umask(umask_val); +/* Remove the running daemon pidfile */ +void +pidfile_rm(pidfile_t *pidf) +{ + unlink(pidf->path); + if (pidf->fd != -1) + pidfile_close(pidf, true); +} - if (pidfd != -1) - pidfile = fdopen(pidfd, "w"); +void +close_other_pidfiles(void) +{ + if (prog_type != PROG_TYPE_PARENT) + pidfile_close(&main_pidfile, true); - if (!pidfile) { - log_message(LOG_INFO, "pidfile_write : Cannot open %s pidfile", - pid_file); - return false; - } +#ifdef _WITH_VRRP_ + if (prog_type != PROG_TYPE_VRRP) + pidfile_close(&vrrp_pidfile, true); +#endif - fprintf(pidfile, "%d\n", pid); - fclose(pidfile); +#ifdef _WITH_LVS_ + if (prog_type != PROG_TYPE_CHECKER) + pidfile_close(&checkers_pidfile, true); +#endif - return true; +#ifdef _WITH_BFD_ + if (prog_type != PROG_TYPE_BFD) + pidfile_close(&bfd_pidfile, true); +#endif } -/* Remove the running daemon pidfile */ -void -pidfile_rm(const char *pid_file) +/* Create the running daemon pidfile */ +bool +pidfile_write(const pidfile_t *pidf) { - unlink(pid_file); + if (pidf->fd == -1) + return false; + + dprintf(pidf->fd, "%d\n", getpid()); + + return true; } /* return the daemon running state */ static bool -process_running(const char *pid_file) +create_pidfile(pidfile_t *pidf) { - FILE *pidfile = fopen(pid_file, "r"); - pid_t pid = 0; + struct stat st, fd_st; + int error; int ret; - - /* No pidfile */ - if (!pidfile) + struct flock fl = { .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = 0 }; + + for (;;) { + /* We want to create the file with permissions rw-r--r-- */ + if (umask_val & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) + umask(umask_val & ~(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); + + while ((pidf->fd = open(pidf->path, O_NOFOLLOW | O_CREAT | O_WRONLY | O_NONBLOCK, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1 && errno == EINTR); + error = errno; + + /* Restore the default umask */ + if (umask_val & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) + umask(umask_val); + + if (pidf->fd == -1) { + errno = error; + return true; + } + + fl.l_pid = 0; + while ((ret = fcntl(pidf->fd, F_OFD_SETLK, &fl)) && errno == EINTR); + if (ret) { + if (errno == EAGAIN) + log_message(LOG_INFO, "Another process has pid file %s locked", pidf->path); + else + log_message(LOG_INFO, "Locking pid file %s error %d - %m", pidf->path, errno); + + break; + } + + /* Make sure the file has not been removed/moved */ + if (stat(pidf->path, &st)) { + close(pidf->fd); + pidf->fd = -1; + continue; + } + + if (fstat(pidf->fd, &fd_st)) { + /* This should not happen since we have the file open */ + break; + } + + if (st.st_dev != fd_st.st_dev || + st.st_ino != fd_st.st_ino) { + /* A new file with the same name has been created */ + close(pidf->fd); + pidf->fd = -1; + continue; + } + + while ((ret = ftruncate(pidf->fd, 0)) && errno == EINTR); + if (ret) { + /* This should not happen */ + break; + } + + /* pid file is now openned, locked and 0 length */ return false; - - ret = fscanf(pidfile, "%d", &pid); - fclose(pidfile); - if (ret != 1) { - log_message(LOG_INFO, "Error reading pid file %s", pid_file); - pid = 0; - pidfile_rm(pid_file); } - /* What should we return - we don't know if it is running or not. */ - if (!pid) - return true; - - /* If no process is attached to pidfile, remove it */ - if (kill(pid, 0)) { - log_message(LOG_INFO, "Remove a zombie pid file %s", pid_file); - pidfile_rm(pid_file); - return false; + if (pidf->fd != -1) { + close(pidf->fd); + pidf->fd = -1; } return true; @@ -180,18 +242,18 @@ process_running(const char *pid_file) bool keepalived_running(unsigned long mode) { - if (process_running(main_pidfile)) + if (create_pidfile(&main_pidfile)) return true; #ifdef _WITH_VRRP_ - if (__test_bit(DAEMON_VRRP, &mode) && process_running(vrrp_pidfile)) + if (__test_bit(DAEMON_VRRP, &mode) && create_pidfile(&vrrp_pidfile)) return true; #endif #ifdef _WITH_LVS_ - if (__test_bit(DAEMON_CHECKERS, &mode) && process_running(checkers_pidfile)) + if (__test_bit(DAEMON_CHECKERS, &mode) && create_pidfile(&checkers_pidfile)) return true; #endif #ifdef _WITH_BFD_ - if (__test_bit(DAEMON_BFD, &mode) && process_running(bfd_pidfile)) + if (__test_bit(DAEMON_BFD, &mode) && create_pidfile(&bfd_pidfile)) return true; #endif return false; diff --git a/keepalived/include/main.h b/keepalived/include/main.h index 90809ac13..b0c6bd49b 100644 --- a/keepalived/include/main.h +++ b/keepalived/include/main.h @@ -30,6 +30,7 @@ #include #include "scheduler.h" +#include "pidfile.h" /* State flags */ enum daemon_bits { @@ -58,21 +59,21 @@ extern unsigned long daemon_mode; /* Which child processes are run */ extern const char *conf_file; /* Configuration file */ #ifdef _WITH_VRRP_ extern pid_t vrrp_child; /* VRRP child process ID */ -extern const char *vrrp_pidfile; /* overrule default pidfile */ +extern pidfile_t vrrp_pidfile; /* overrule default pidfile */ extern bool have_vrrp_instances; /* vrrp instances configured */ #endif #ifdef _WITH_LVS_ extern pid_t checkers_child; /* Healthcheckers child process ID */ -extern const char *checkers_pidfile; /* overrule default pidfile */ +extern pidfile_t checkers_pidfile; /* overrule default pidfile */ extern bool have_virtual_servers; /* virtual servers configured */ #endif #ifdef _WITH_BFD_ extern pid_t bfd_child; /* BFD child process ID */ -extern const char *bfd_pidfile; /* overrule default pidfile */ +extern pidfile_t bfd_pidfile; /* overrule default pidfile */ extern bool have_bfd_instances; /* bfd instances configured */ #endif extern bool reload; /* Set during a reload */ -extern const char *main_pidfile; /* overrule default pidfile */ +extern pidfile_t main_pidfile; /* overrule default pidfile */ #ifdef _WITH_SNMP_ extern bool snmp_option; /* Enable SNMP support */ extern const char *snmp_socket; /* Socket to use for SNMP agent */ diff --git a/keepalived/include/pidfile.h b/keepalived/include/pidfile.h index 9684c2bf8..068a1db98 100644 --- a/keepalived/include/pidfile.h +++ b/keepalived/include/pidfile.h @@ -45,14 +45,22 @@ #define PID_EXTENSION ".pid" #define RELOAD_EXTENSION ".reload" +typedef struct pidfile { + const char * path; + bool free_path; + int fd; +} pidfile_t; + extern const char *pid_directory; /* Prototypes */ extern void create_pid_dir(void); extern void remove_pid_dir(void); extern char *make_pidfile_name(const char *, const char *, const char *); -extern bool pidfile_write(const char *, int); -extern void pidfile_rm(const char *); +extern void pidfile_close(pidfile_t *, bool); +extern bool pidfile_write(const pidfile_t *); +extern void pidfile_rm(pidfile_t *); +extern void close_other_pidfiles(void); extern bool keepalived_running(unsigned long); #endif diff --git a/keepalived/vrrp/vrrp_daemon.c b/keepalived/vrrp/vrrp_daemon.c index 4a483d40c..c339ceca3 100644 --- a/keepalived/vrrp/vrrp_daemon.c +++ b/keepalived/vrrp/vrrp_daemon.c @@ -351,7 +351,7 @@ vrrp_terminate_phase2(int exit_status) close_std_fd(); /* Stop daemon */ - pidfile_rm(vrrp_pidfile); + pidfile_rm(&vrrp_pidfile); exit(exit_status); } @@ -1074,6 +1074,8 @@ start_vrrp_child(void) deregister_thread_addresses(); #endif + close_other_pidfiles(); + #ifdef _WITH_BFD_ /* Close the write end of the BFD vrrp event notification pipe */ close(bfd_vrrp_event_pipe[1]); @@ -1115,7 +1117,7 @@ start_vrrp_child(void) separate_config_file(); /* Child process part, write pidfile */ - if (!pidfile_write(vrrp_pidfile, getpid())) { + if (!pidfile_write(&vrrp_pidfile)) { /* Fatal error */ log_message(LOG_INFO, "VRRP child process: cannot write pidfile"); exit(0);