Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ AC_ARG_ENABLE(regex-timers,
[AS_HELP_STRING([--enable-regex-timers], [build with HTTP_GET regex timers])])
AC_ARG_ENABLE(json,
[AS_HELP_STRING([--enable-json], [compile with signal to dump configuration and stats as json])])
AC_ARG_ENABLE(status-socket,
[AS_HELP_STRING([--enable-status-socket], [enable unix socket for status queries and health checks])])
AC_ARG_ENABLE(clang,
[AS_HELP_STRING([--enable-clang], [use clang compiler])])
AC_ARG_ENABLE(lto,
Expand Down Expand Up @@ -2372,6 +2374,7 @@ VRRP_SUPPORT=No
VRRP_AUTH_SUPPORT=No
MACVLAN_SUPPORT=No
ENABLE_JSON=No
STATUS_SOCKET_SUPPORT=No
BFD_SUPPORT=No
HAVE_CN_PROC=No
WITH_TRACK_PROCESS=No
Expand Down Expand Up @@ -2436,6 +2439,13 @@ if test "$enable_vrrp" != no; then
add_config_opt([JSON])
fi

dnl ----[ Status socket support ? ]----
if test "${enable_status_socket}" = yes; then
STATUS_SOCKET_SUPPORT=Yes
AC_DEFINE([_WITH_STATUS_SOCKET_], [ 1 ], [Define to 1 to enable status socket for health checks])
add_config_opt([STATUS_SOCKET])
fi

dnl ----[ BFD support ? ]----
if test "${enable_bfd}" = yes; then
BFD_SUPPORT=Yes
Expand All @@ -2454,6 +2464,7 @@ AM_CONDITIONAL([WITH_VRRP], [test $VRRP_SUPPORT = Yes])
AM_CONDITIONAL([VRRP_AUTH], [test $VRRP_AUTH_SUPPORT = Yes])
AM_CONDITIONAL([VMAC], [test $MACVLAN_SUPPORT = Yes])
AM_CONDITIONAL([WITH_JSON], [test $ENABLE_JSON = Yes])
AM_CONDITIONAL([WITH_STATUS_SOCKET], [test $STATUS_SOCKET_SUPPORT = Yes])
AM_CONDITIONAL([WITH_BFD], [test $BFD_SUPPORT = Yes])
AM_CONDITIONAL([TRACK_PROCESS], [test $WITH_TRACK_PROCESS = Yes])

Expand Down
22 changes: 22 additions & 0 deletions doc/man/man5/keepalived.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,28 @@ possibly following any cleanup actions needed.
# (default: "none")
\fBdbus_no_interface_name \fRNAME

# If Keepalived has been built with status socket support
# (--enable-status-socket), the following keywords are available.
# --
# Enable a Unix domain socket for unified status queries and health checks.
# The socket runs in the parent process and aggregates state from all
# child daemons (VRRP, Checker, BFD).
# The socket accepts simple text commands:
# HEALTH - returns "MASTER", "BACKUP", or "FAULT" based on aggregated state
# FAULT if any daemon reports fault state
# MASTER if VRRP has at least one instance in MASTER state
# BACKUP otherwise
# STATUS - returns JSON with details from all running daemons:
# {"vrrp":{...},"checker":{...},"bfd":{...}}
# Useful for container health checks (e.g., Kubernetes liveness probes).
# Example: echo "HEALTH" | socat - UNIX:/var/run/keepalived/status.sock
# (default: /var/run/keepalived/status.sock)
\fBstatus_socket \fRPATH

# Unix file permissions for the status socket
# (default: 0600)
\fBstatus_socket_mode \fRMODE

# Specify the default username/groupname to run scripts under.
# If this option is not specified, the user defaults to keepalived_script
# if that user exists, otherwise the uid/gid under which keepalived is running.
Expand Down
32 changes: 32 additions & 0 deletions keepalived/bfd/bfd_daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
#ifndef _ONE_PROCESS_DEBUG_
#include "config_notify.h"
#endif
#ifdef _WITH_STATUS_SOCKET_
#include "status_event.h"
#endif


/* Global variables */
Expand Down Expand Up @@ -427,6 +430,35 @@ start_bfd_child(void)
close(bfd_checker_event_pipe[0]);
#endif

#ifdef _WITH_STATUS_SOCKET_
/* BFD child keeps only write end of its status pipe */
if (status_bfd_pipe[0] >= 0) {
close(status_bfd_pipe[0]);
status_bfd_pipe[0] = -1;
}
/* Close all of VRRP and checker status pipes */
#ifdef _WITH_VRRP_
if (status_vrrp_pipe[0] >= 0) {
close(status_vrrp_pipe[0]);
status_vrrp_pipe[0] = -1;
}
if (status_vrrp_pipe[1] >= 0) {
close(status_vrrp_pipe[1]);
status_vrrp_pipe[1] = -1;
}
#endif
#ifdef _WITH_LVS_
if (status_checker_pipe[0] >= 0) {
close(status_checker_pipe[0]);
status_checker_pipe[0] = -1;
}
if (status_checker_pipe[1] >= 0) {
close(status_checker_pipe[1]);
status_checker_pipe[1] = -1;
}
#endif
#endif

#ifdef THREAD_DUMP
/* Remove anything we might have inherited from parent */
deregister_thread_addresses();
Expand Down
36 changes: 36 additions & 0 deletions keepalived/bfd/bfd_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,45 @@
#include "bfd.h"
#include "bfd_event.h"
#include "bfd_daemon.h"
#include "bfd_data.h"
#include "logger.h"
#include "main.h"
#include "memory.h"
#include "bitops.h"
#include "utils.h"
#include "global_data.h"
#include "assert_debug.h"
#ifdef _WITH_STATUS_SOCKET_
#include "status_event.h"
#include "list_head.h"

static void
bfd_send_status_event(void)
{
bfd_t *bfd;
uint32_t num_inst = 0;
uint32_t num_down = 0;
uint8_t state;

if (!bfd_data || list_empty(&bfd_data->bfd))
return;

list_for_each_entry(bfd, &bfd_data->bfd, e_list) {
num_inst++;
if (bfd->local_state != BFD_STATE_UP)
num_down++;
}

if (num_inst == 0)
state = STATUS_STATE_INIT;
else if (num_down > 0)
state = (num_down == num_inst) ? STATUS_STATE_FAULT : STATUS_STATE_DOWN;
else
state = STATUS_STATE_UP;

status_send_bfd_event(state, num_inst, num_down);
}
#endif

void
bfd_event_send(bfd_t *bfd)
Expand Down Expand Up @@ -82,4 +114,8 @@ bfd_event_send(bfd_t *bfd)
bfd->iname);
}
#endif

#ifdef _WITH_STATUS_SOCKET_
bfd_send_status_event();
#endif
}
32 changes: 32 additions & 0 deletions keepalived/check/check_daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@
#ifndef _ONE_PROCESS_DEBUG_
#include "config_notify.h"
#endif
#ifdef _WITH_STATUS_SOCKET_
#include "status_event.h"
#endif

/* Global variables */
bool using_ha_suspend;
Expand Down Expand Up @@ -737,6 +740,35 @@ start_check_child(void)
close_track_processes();
#endif

#ifdef _WITH_STATUS_SOCKET_
/* Checker child keeps only write end of its status pipe */
if (status_checker_pipe[0] >= 0) {
close(status_checker_pipe[0]);
status_checker_pipe[0] = -1;
}
/* Close all of VRRP and BFD status pipes */
#ifdef _WITH_VRRP_
if (status_vrrp_pipe[0] >= 0) {
close(status_vrrp_pipe[0]);
status_vrrp_pipe[0] = -1;
}
if (status_vrrp_pipe[1] >= 0) {
close(status_vrrp_pipe[1]);
status_vrrp_pipe[1] = -1;
}
#endif
#ifdef _WITH_BFD_
if (status_bfd_pipe[0] >= 0) {
close(status_bfd_pipe[0]);
status_bfd_pipe[0] = -1;
}
if (status_bfd_pipe[1] >= 0) {
close(status_bfd_pipe[1]);
status_bfd_pipe[1] = -1;
}
#endif
#endif

if ((global_data->instance_name || global_data->network_namespace) &&
(check_syslog_ident = make_syslog_ident(PROG_CHECK)))
syslog_ident = check_syslog_ident;
Expand Down
39 changes: 39 additions & 0 deletions keepalived/check/ipwrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,41 @@
#include "check_nftables.h"
#include "check_data.h"
#endif
#ifdef _WITH_STATUS_SOCKET_
#include "status_event.h"
#endif

#ifdef _WITH_STATUS_SOCKET_
static void
checker_send_status_event(void)
{
virtual_server_t *vs;
real_server_t *rs;
uint32_t num_rs = 0;
uint32_t num_down = 0;
uint8_t state;

if (!check_data)
return;

list_for_each_entry(vs, &check_data->vs, e_list) {
list_for_each_entry(rs, &vs->rs, e_list) {
num_rs++;
if (!ISALIVE(rs))
num_down++;
}
}

if (num_rs == 0)
state = STATUS_STATE_INIT;
else if (num_down > 0)
state = (num_down == num_rs) ? STATUS_STATE_FAULT : STATUS_STATE_DOWN;
else
state = STATUS_STATE_UP;

status_send_checker_event(state, num_rs, num_down);
}
#endif

static bool __attribute((pure))
vs_iseq(const virtual_server_t *vs_a, const virtual_server_t *vs_b)
Expand Down Expand Up @@ -621,6 +656,10 @@ perform_svr_state(bool alive, checker_t *checker)
* but is now up, this is where the rs is added. */
update_quorum_state(vs, false);

#ifdef _WITH_STATUS_SOCKET_
checker_send_status_event();
#endif

return true;
}

Expand Down
5 changes: 5 additions & 0 deletions keepalived/core/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,9 @@ if WITH_SANITIZER
EXTRA_libcore_a_SOURCES += sanitizer.c
endif

if WITH_STATUS_SOCKET
libcore_a_LIBADD += status_socket.o
EXTRA_libcore_a_SOURCES += status_socket.c
endif

MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@
8 changes: 8 additions & 0 deletions keepalived/core/global_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ free_global_data(data_t **datap)
FREE_CONST_PTR(data->dbus_service_name);
FREE_CONST_PTR(data->dbus_no_interface_name);
#endif
#ifdef _WITH_STATUS_SOCKET_
FREE_CONST_PTR(data->status_socket_path);
#endif
#ifndef _ONE_PROCESS_DEBUG_
FREE_CONST_PTR(data->reload_check_config);
FREE_CONST_PTR(data->reload_file);
Expand Down Expand Up @@ -920,6 +923,11 @@ dump_global_data(FILE *fp, data_t * data)
conf_write(fp, " DBus %s", data->enable_dbus ? "enabled" : "disabled");
conf_write(fp, " DBus service name = %s", data->dbus_service_name ? data->dbus_service_name : "");
conf_write(fp, " DBus no interface name = %s", data->dbus_no_interface_name ? data->dbus_no_interface_name : dbus_no_interface_name);
#endif
#ifdef _WITH_STATUS_SOCKET_
conf_write(fp, " Status socket %s", data->enable_status_socket ? "enabled" : "disabled");
if (data->status_socket_path)
conf_write(fp, " Status socket path = %s", data->status_socket_path);
#endif
conf_write(fp, " Script security %s", script_security ? "enabled" : "disabled");
if (!get_default_script_user(&uid, &gid))
Expand Down
37 changes: 37 additions & 0 deletions keepalived/core/global_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,39 @@ dbus_no_interface_name_handler(const vector_t *strvec)
}
#endif

#ifdef _WITH_STATUS_SOCKET_
static void
status_socket_handler(const vector_t *strvec)
{
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "status_socket requires path - ignoring");
return;
}

FREE_CONST_PTR(global_data->status_socket_path);
global_data->status_socket_path = set_value(strvec);
global_data->enable_status_socket = true;
}

static void
status_socket_mode_handler(const vector_t *strvec)
{
unsigned mode;

if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "status_socket_mode requires mode value - ignoring");
return;
}

if (!read_unsigned(strvec_slot(strvec, 1), &mode, 0, 0777, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "status_socket_mode %s invalid - ignoring", strvec_slot(strvec, 1));
return;
}

global_data->status_socket_mode = mode;
}
#endif

static void
instance_handler(const vector_t *strvec)
{
Expand Down Expand Up @@ -2754,6 +2787,10 @@ init_global_keywords(bool global_active)
install_keyword("enable_dbus", &enable_dbus_handler);
install_keyword("dbus_service_name", &dbus_service_name_handler);
install_keyword("dbus_no_interface_name", &dbus_no_interface_name_handler);
#endif
#ifdef _WITH_STATUS_SOCKET_
install_keyword("status_socket", &status_socket_handler);
install_keyword("status_socket_mode", &status_socket_mode_handler);
#endif
install_keyword("script_user", &script_user_handler);
install_keyword("enable_script_security", &script_security_handler);
Expand Down
Loading