diff --git a/doc/man/man5/keepalived.conf.5.in b/doc/man/man5/keepalived.conf.5.in index 64eb70d01d..18bc4766c0 100644 --- a/doc/man/man5/keepalived.conf.5.in +++ b/doc/man/man5/keepalived.conf.5.in @@ -964,8 +964,25 @@ possibly following any cleanup actions needed. # if more than one keepalived instance is running on a system. # In order to alleviate this, enabling data_use_instance includes the # instance name and network namespace in the file name of the .data files. + # This also applies to SIGUSR2 for outputting stats. \fBdata_use_instance \fR[] + # If the files produced by SIGUSR1 and SIGUSR2 are on slow storage, then writing to + # them may cause keepalived processes to block. This option allows specifying the + # location to which the files should be written, and should ideally be a tmpfs rather + # than a real disc, and definitely not network attached storage or other storage that + # can block for more than a few microseconds. + # If path ends in a / it is considered to be a directory and the normal filenames + # in that directory will be used, otherwise it will be used as the full path name + # of the file to be written. Note that \fBdata_use_instance\fR may also modify the + # file name. + # For the state files, these are template names and "_checker" will be added + # for the checker process, "_bfd" will be added for the BFD process and + # "_parent" will be added for the parent process. + \fBstate_file_location \fRpath + \fBstats_file_location \fRpath + \fBjson_file_location \fRpath + # json_version 2 puts the VRRP data in a named array and adds # track_process details. Default is version 1. \fBjson_version \fR{1|2} diff --git a/doc/man/man8/keepalived.8.in b/doc/man/man8/keepalived.8.in index 9f00ad2ddd..124fa0295b 100644 --- a/doc/man/man8/keepalived.8.in +++ b/doc/man/man8/keepalived.8.in @@ -372,20 +372,20 @@ will shut down. .TP .B USR1\fP or \fBSIGFUNC=DATA Write configuration data to -.B @KA_TMP_DIR@/keepalived.data +.B @KA_TMP_DIR@/keepalived.data or file configured by state_dump_file .TP .B USR2\fP or \fBSIGFUNC=STATS Write statistics info to -.B @KA_TMP_DIR@/keepalived.stats +.B @KA_TMP_DIR@/keepalived.stats or file configured by stats_dump_file .TP .B SIGFUNC=STATS_CLEAR Write statistics info to -.B @KA_TMP_DIR@/keepalived.stats +.B @KA_TMP_DIR@/keepalived.stats or file configured by stats_dump_file and clear the statistics counters .TP .B SIGFUNC=JSON Write configuration data in JSON format to -.B @KA_TMP_DIR@/keepalived.json +.B @KA_TMP_DIR@/keepalived.json or file configured by json_dump_file .TP .B SIGFUNC=TDATA This causes diff --git a/keepalived/bfd/bfd_data.c b/keepalived/bfd/bfd_data.c index 6f0953806c..747b35aa0a 100644 --- a/keepalived/bfd/bfd_data.c +++ b/keepalived/bfd/bfd_data.c @@ -299,7 +299,7 @@ bfd_print_data(void) { FILE *fp; - fp = open_dump_file("keepalived_bfd.data"); + fp = open_dump_file("_bfd"); if (!fp) return; diff --git a/keepalived/check/check_print.c b/keepalived/check/check_print.c index ecc354d959..804a9026b9 100644 --- a/keepalived/check/check_print.c +++ b/keepalived/check/check_print.c @@ -38,7 +38,7 @@ check_print_data(void) { FILE *fp; - fp = open_dump_file("keepalived_check.data"); + fp = open_dump_file("_check"); if (!fp) return; diff --git a/keepalived/core/global_data.c b/keepalived/core/global_data.c index 679b2bef95..045bdb2db5 100644 --- a/keepalived/core/global_data.c +++ b/keepalived/core/global_data.c @@ -516,46 +516,78 @@ free_global_data(data_t **datap) FREE_CONST_PTR(data->iproute_usr_dir); FREE_CONST_PTR(data->iproute_etc_dir); #endif + FREE_CONST_PTR(data->state_dump_file); + FREE_CONST_PTR(data->stats_dump_file); + FREE_CONST_PTR(data->json_dump_file); + FREE(data); *datap = NULL; } FILE * __attribute__((malloc)) -open_dump_file(const char *file_name) +open_dump_file(const char *default_file_name) { FILE *fp; - const char *full_file_name; - char *tmp_file_name; + const char *file_name; + char *full_file_name; const char *dot; - int len; + size_t len; + const char *dir; + size_t dir_len; + + /* + * If no leading /, use tmp_dir + * If trailing /, add "keepalived%s.data", default_file_name + */ + + if (global_data->state_dump_file && + global_data->state_dump_file[0] == '/') { + dir = global_data->state_dump_file; + dir_len = strlen(dir); + if (dir[dir_len - 1] != '/') + dir_len = strrchr(dir, '/') - dir; + } else { + dir = tmp_dir; + dir_len = strlen(tmp_dir); + } - if (global_data->data_use_instance && - (global_data->instance_name || global_data->network_namespace)) { - len = strlen(tmp_dir) + 1 + strlen(file_name) + 1; + if (global_data->state_dump_file && + global_data->state_dump_file[strlen(global_data->state_dump_file) - 1] != '/') { + if (!(file_name = strrchr(global_data->state_dump_file, '/'))) + file_name = global_data->state_dump_file; + else + file_name++; /* Skip to last '/' */ + } else + file_name = "keepalived.data"; + + if (!(dot = strrchr(file_name, '.'))) + dot = file_name + strlen(file_name); + + len = dir_len + 1 + strlen(file_name) + 1 + strlen(default_file_name); + if (global_data->data_use_instance) { if (global_data->instance_name) len += strlen(global_data->instance_name) + 1; if (global_data->network_namespace) len += strlen(global_data->network_namespace) + 1; + } - tmp_file_name = MALLOC(len); + full_file_name = MALLOC(len); - dot = strrchr(file_name, '.'); - sprintf(tmp_file_name, "%s/%.*s.%s%s%s%s", tmp_dir, - (int)(dot - file_name), file_name, - global_data->network_namespace ? global_data->network_namespace : "", - global_data->instance_name && global_data->network_namespace ? "_" : "", - global_data->instance_name ? global_data->instance_name : "", - dot); - full_file_name = tmp_file_name; - } else - full_file_name = make_tmp_filename(file_name); + snprintf(full_file_name, len, "%.*s/%.*s%s%s%s%s%s%s", (int)dir_len, dir, + (int)(dot - file_name), file_name, + default_file_name, + global_data->data_use_instance && (global_data->instance_name || global_data->network_namespace) ? "." : "", + global_data->data_use_instance && global_data->network_namespace ? global_data->network_namespace : "", + global_data->data_use_instance && global_data->instance_name && global_data->network_namespace ? "_" : "", + global_data->data_use_instance && global_data->instance_name ? global_data->instance_name : "", + dot); fp = fopen_safe(full_file_name, "w"); if (!fp) log_message(LOG_INFO, "Can't open dump file %s (%d: %s)", - file_name, errno, strerror(errno)); + full_file_name, errno, strerror(errno)); FREE_CONST(full_file_name); @@ -941,4 +973,10 @@ dump_global_data(FILE *fp, data_t * data) conf_write(fp, " iproute usr directory %s", global_data->iproute_usr_dir ? global_data->iproute_usr_dir : "(none)"); conf_write(fp, " iproute etc directory %s", global_data->iproute_etc_dir ? global_data->iproute_etc_dir : "(none)"); #endif + if (global_data->state_dump_file) + conf_write(fp, " state dump file %s", global_data->state_dump_file); + if (global_data->stats_dump_file) + conf_write(fp, " stats dump file %s", global_data->stats_dump_file); + if (global_data->json_dump_file) + conf_write(fp, " json dump file %s", global_data->json_dump_file); } diff --git a/keepalived/core/global_parser.c b/keepalived/core/global_parser.c index 0efbb6bba6..8c154b965e 100644 --- a/keepalived/core/global_parser.c +++ b/keepalived/core/global_parser.c @@ -2469,6 +2469,42 @@ iproute_etc_handler(const vector_t *strvec) } #endif +static void +state_dump_file_handler(const vector_t *strvec) +{ + if (vector_size(strvec) != 2 || + !strvec_slot(strvec, 1)[0]) { + report_config_error(CONFIG_GENERAL_ERROR, "%s requires a non-empty path", strvec_slot(strvec, 0)); + return; + } + + global_data->state_dump_file = STRDUP(strvec_slot(strvec, 1)); +} + +static void +stats_dump_file_handler(const vector_t *strvec) +{ + if (vector_size(strvec) != 2 || + !strvec_slot(strvec, 1)[0]) { + report_config_error(CONFIG_GENERAL_ERROR, "%s requires a non-empty path", strvec_slot(strvec, 0)); + return; + } + + global_data->stats_dump_file = STRDUP(strvec_slot(strvec, 1)); +} + +static void +json_dump_file_handler(const vector_t *strvec) +{ + if (vector_size(strvec) != 2 || + !strvec_slot(strvec, 1)[0]) { + report_config_error(CONFIG_GENERAL_ERROR, "%s requires a non-empty path", strvec_slot(strvec, 0)); + return; + } + + global_data->json_dump_file = STRDUP(strvec_slot(strvec, 1)); +} + void init_global_keywords(bool global_active) { @@ -2688,4 +2724,7 @@ init_global_keywords(bool global_active) install_keyword("iproute_usr_dir", &iproute_usr_handler); install_keyword("iproute_etc_dir", &iproute_etc_handler); #endif + install_keyword("state_dump_file", &state_dump_file_handler); + install_keyword("stats_dump_file", &stats_dump_file_handler); + install_keyword("json_dump_file", &json_dump_file_handler); } diff --git a/keepalived/core/main.c b/keepalived/core/main.c index 52de890b38..afa9be6949 100644 --- a/keepalived/core/main.c +++ b/keepalived/core/main.c @@ -876,7 +876,7 @@ print_parent_data(__attribute__((unused)) thread_ref_t thread) log_message(LOG_INFO, "Printing parent data for process(%d) on signal", getpid()); - fp = open_dump_file("keepalived_parent.data"); + fp = open_dump_file("_parent"); if (!fp) return; diff --git a/keepalived/include/global_data.h b/keepalived/include/global_data.h index 7024eb6a3f..b6c2d08231 100644 --- a/keepalived/include/global_data.h +++ b/keepalived/include/global_data.h @@ -298,6 +298,9 @@ typedef struct _data { const char *iproute_usr_dir; const char *iproute_etc_dir; #endif + const char *state_dump_file; + const char *stats_dump_file; + const char *json_dump_file; } data_t; /* Global vars exported */ diff --git a/keepalived/vrrp/vrrp_print.c b/keepalived/vrrp/vrrp_print.c index 8ad1c0846f..3f2ad83029 100644 --- a/keepalived/vrrp/vrrp_print.c +++ b/keepalived/vrrp/vrrp_print.c @@ -42,7 +42,7 @@ vrrp_print_data(void) { FILE *fp; - fp = open_dump_file("keepalived.data"); + fp = open_dump_file(""); if (!fp) return; diff --git a/lib/rttables.c b/lib/rttables.c index f1c960c086..bf8cae467e 100644 --- a/lib/rttables.c +++ b/lib/rttables.c @@ -605,7 +605,7 @@ write_addrproto_config(const char *name, uint32_t val) bool file_exists = false; struct stat statbuf; - fp = popen("ip -V", "re"); + fp = popen("ip -V 2>&1", "re"); res = fgets(buf, sizeof(buf), fp); pclose(fp); @@ -616,14 +616,25 @@ write_addrproto_config(const char *name, uint32_t val) * ip utility, iproute2-5.10.0 * or * ip utility, iproute2-6.7.0, libbpf 1.2.3 - */ + * or + * BusyBox v1.36.1 (2024-06-10 07:11:47 UTC) multi-call binary + * + * Busybox does not support the iproute2 configuration files. + */ + if (!strstr(buf, "iproute2")) + return; + if (strstr(buf, "BusyBox")) + return; + if (!(v = strchr(buf, '-'))) return; v++; if ((e = strchr(v, ','))) *e = '\0'; - sscanf(v, "%d.%d.%d", &ver_maj, &ver_min, &ver_rel); + if (sscanf(v, "%d.%d.%d", &ver_maj, &ver_min, &ver_rel) != 3) + return; + if (ver_maj >= 7 || (ver_maj == 6 && ver_min >= 12)) { dir = IPROUTE_ETC_DIR "/" RT_ADDRPROTOS_FILE ".d"; path = IPROUTE_ETC_DIR "/" RT_ADDRPROTOS_FILE ".d/keepalived.conf" ;