diff --git a/.gitignore b/.gitignore index 757faefe..2791c583 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,3 @@ repmgr repmgrd repmgr4 repmgrd4 - -# generated files -configfile-scan.c diff --git a/HISTORY b/HISTORY index 9dc01de3..4da2f336 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,3 @@ -4.5 2019-??-?? - general: parse configuration file using flex (Ian) - 4.4 2019-06-27 repmgr: improve "daemon status" output (Ian) repmgr: add "--siblings-follow" option to "standby promote" (Ian) diff --git a/Makefile.global.in b/Makefile.global.in index 43ad32f9..79e32e9d 100644 --- a/Makefile.global.in +++ b/Makefile.global.in @@ -2,7 +2,6 @@ # Makefile.global.in # @configure_input@ - # Can only be built using pgxs USE_PGXS=1 @@ -27,11 +26,3 @@ include $(PGXS) REPMGR_VERSION=$(shell awk '/^\#define REPMGR_VERSION / { print $3; }' ${repmgr_abs_srcdir}/repmgr_version.h.in | cut -d '"' -f 2) REPMGR_RELEASE_DATE=$(shell awk '/^\#define REPMGR_RELEASE_DATE / { print $3; }' ${repmgr_abs_srcdir}/repmgr_version.h.in | cut -d '"' -f 2) -FLEX = flex - -########################################################################## -# -# Global targets and rules - -%.c: %.l - $(FLEX) $(FLEXFLAGS) -o'$@' $< diff --git a/Makefile.in b/Makefile.in index 325703dd..70df4036 100644 --- a/Makefile.in +++ b/Makefile.in @@ -54,15 +54,13 @@ $(info Building against PostgreSQL $(MAJORVERSION)) REPMGR_CLIENT_OBJS = repmgr-client.o \ repmgr-action-primary.o repmgr-action-standby.o repmgr-action-witness.o \ repmgr-action-bdr.o repmgr-action-cluster.o repmgr-action-node.o repmgr-action-daemon.o \ - configfile.o configfile-scan.o log.o strutil.o controldata.o dirutil.o compat.o dbutils.o sysutils.o -REPMGRD_OBJS = repmgrd.o repmgrd-physical.o repmgrd-bdr.o configfile.o configfile-scan.o log.o dbutils.o strutil.o controldata.o compat.o sysutils.o + configfile.o log.o strutil.o controldata.o dirutil.o compat.o dbutils.o sysutils.o +REPMGRD_OBJS = repmgrd.o repmgrd-physical.o repmgrd-bdr.o configfile.o log.o dbutils.o strutil.o controldata.o compat.o sysutils.o DATE=$(shell date "+%Y-%m-%d") repmgr_version.h: repmgr_version.h.in sed '0,/REPMGR_VERSION_DATE/s,\(REPMGR_VERSION_DATE\).*,\1 "$(DATE)",' $< >$@ -configfile-scan.c: configfile-scan.l - $(REPMGR_CLIENT_OBJS): repmgr-client.h repmgr_version.h repmgr: $(REPMGR_CLIENT_OBJS) diff --git a/configfile-scan.l b/configfile-scan.l deleted file mode 100644 index a1ef07b5..00000000 --- a/configfile-scan.l +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Scanner for the configuration file - */ - -%{ - -#define FRONTEND - -#include "postgres.h" - -#include "repmgr.h" -#include "configfile.h" - -/* - * flex emits a yy_fatal_error() function that it calls in response to - * critical errors like malloc failure, file I/O errors, and detection of - * internal inconsistency. That function prints a message and calls exit(). - * Mutate it to instead call our handler, which jumps out of the parser. - */ -#undef fprintf -#define fprintf(file, fmt, msg) CONF_flex_fatal(msg) - -enum -{ - CONF_ID = 1, - CONF_STRING = 2, - CONF_INTEGER = 3, - CONF_REAL = 4, - CONF_EQUALS = 5, - CONF_UNQUOTED_STRING = 6, - CONF_QUALIFIED_ID = 7, - CONF_EOL = 99, - CONF_ERROR = 100 -}; - -static unsigned int ConfigFileLineno; -static const char *CONF_flex_fatal_errmsg; -static sigjmp_buf *CONF_flex_fatal_jmp; - -static char *CONF_scanstr(const char *s); -static int CONF_flex_fatal(const char *msg); - -%} - -%option 8bit -%option never-interactive -%option nodefault -%option noinput -%option nounput -%option noyywrap -%option warn -%option prefix="CONF_yy" - - -SIGN ("-"|"+") -DIGIT [0-9] -HEXDIGIT [0-9a-fA-F] - -UNIT_LETTER [a-zA-Z] - -INTEGER {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}* - -EXPONENT [Ee]{SIGN}?{DIGIT}+ -REAL {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}? - -LETTER [A-Za-z_\200-\377] -LETTER_OR_DIGIT [A-Za-z_0-9\200-\377] - -ID {LETTER}{LETTER_OR_DIGIT}* -QUALIFIED_ID {ID}"."{ID} - -UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])* -STRING \'([^'\\\n]|\\.|\'\')*\' - -%% - -\n ConfigFileLineno++; return CONF_EOL; -[ \t\r]+ /* eat whitespace */ -#.* /* eat comment (.* matches anything until newline) */ - -{ID} return CONF_ID; -{QUALIFIED_ID} return CONF_QUALIFIED_ID; -{STRING} return CONF_STRING; -{UNQUOTED_STRING} return CONF_UNQUOTED_STRING; -{INTEGER} return CONF_INTEGER; -{REAL} return CONF_REAL; -= return CONF_EQUALS; - -. return CONF_ERROR; - -%% - - -extern bool -ProcessConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, ItemList *error_list, ItemList *warning_list) -{ - volatile bool OK = true; - volatile YY_BUFFER_STATE lex_buffer = NULL; - sigjmp_buf flex_fatal_jmp; - int errorcount; - int token; - - if (sigsetjmp(flex_fatal_jmp, 1) == 0) - { - CONF_flex_fatal_jmp = &flex_fatal_jmp; - } - else - { - /* - * Regain control after a fatal, internal flex error. It may have - * corrupted parser state. Consequently, abandon the file, but trust - * that the state remains sane enough for yy_delete_buffer(). - */ - item_list_append_format(error_list, - "%s at file \"%s\" line %u", - CONF_flex_fatal_errmsg, config_file, ConfigFileLineno); - OK = false; - goto cleanup; - } - - /* - * Parse - */ - ConfigFileLineno = 1; - errorcount = 0; - - lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE); - yy_switch_to_buffer(lex_buffer); - - /* This loop iterates once per logical line */ - while ((token = yylex())) - { - char *opt_name = NULL; - char *opt_value = NULL; - - if (token == CONF_EOL) /* empty or comment line */ - continue; - - /* first token on line is option name */ - if (token != CONF_ID && token != CONF_QUALIFIED_ID) - goto parse_error; - opt_name = pstrdup(yytext); - - /* next we have an optional equal sign; discard if present */ - token = yylex(); - if (token == CONF_EQUALS) - token = yylex(); - - /* now we must have the option value */ - if (token != CONF_ID && - token != CONF_STRING && - token != CONF_INTEGER && - token != CONF_REAL && - token != CONF_UNQUOTED_STRING) - goto parse_error; - if (token == CONF_STRING) /* strip quotes and escapes */ - opt_value = CONF_scanstr(yytext); - else - opt_value = pstrdup(yytext); - - /* now we'd like an end of line, or possibly EOF */ - token = yylex(); - if (token != CONF_EOL) - { - if (token != 0) - goto parse_error; - /* treat EOF like \n for line numbering purposes, cf bug 4752 */ - ConfigFileLineno++; - } - - /* OK, process the option name and value */ - - parse_configuration_item(options, - error_list, - warning_list, - opt_name, - opt_value); - - /* break out of loop if read EOF, else loop for next line */ - if (token == 0) - break; - continue; - -parse_error: - /* release storage if we allocated any on this line */ - if (opt_name) - pfree(opt_name); - if (opt_value) - pfree(opt_value); - - /* report the error */ - if (token == CONF_EOL || token == 0) - { - item_list_append_format(error_list, - _("syntax error in file \"%s\" line %u, near end of line"), - config_file, ConfigFileLineno - 1); - } - else - { - item_list_append_format(error_list, - _("syntax error in file \"%s\" line %u, near token \"%s\""), - config_file, ConfigFileLineno, yytext); - } - OK = false; - errorcount++; - - /* - * To avoid producing too much noise when fed a totally bogus file, - * give up after 100 syntax errors per file (an arbitrary number). - * Also, if we're only logging the errors at DEBUG level anyway, might - * as well give up immediately. (This prevents postmaster children - * from bloating the logs with duplicate complaints.) - */ - if (errorcount >= 100) - { - fprintf(stderr, - _("too many syntax errors found, abandoning file \"%s\"\n"), - config_file); - break; - } - - /* resync to next end-of-line or EOF */ - while (token != CONF_EOL && token != 0) - token = yylex(); - /* break out of loop on EOF */ - if (token == 0) - break; - } - -cleanup: - yy_delete_buffer(lex_buffer); - - return OK; -} - - -/* - * scanstr - * - * Strip the quotes surrounding the given string, and collapse any embedded - * '' sequences and backslash escapes. - * - * the string returned is palloc'd and should eventually be pfree'd by the - * caller. - */ -static char * -CONF_scanstr(const char *s) -{ - char *newStr; - int len, - i, - j; - - Assert(s != NULL && s[0] == '\''); - len = strlen(s); - Assert(s != NULL); - - Assert(len >= 2); - Assert(s[len - 1] == '\''); - - /* Skip the leading quote; we'll handle the trailing quote below */ - s++, len--; - - /* Since len still includes trailing quote, this is enough space */ - newStr = palloc(len); - - for (i = 0, j = 0; i < len; i++) - { - if (s[i] == '\\') - { - i++; - switch (s[i]) - { - case 'b': - newStr[j] = '\b'; - break; - case 'f': - newStr[j] = '\f'; - break; - case 'n': - newStr[j] = '\n'; - break; - case 'r': - newStr[j] = '\r'; - break; - case 't': - newStr[j] = '\t'; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - { - int k; - long octVal = 0; - - for (k = 0; - s[i + k] >= '0' && s[i + k] <= '7' && k < 3; - k++) - octVal = (octVal << 3) + (s[i + k] - '0'); - i += k - 1; - newStr[j] = ((char) octVal); - } - break; - default: - newStr[j] = s[i]; - break; - } /* switch */ - } - else if (s[i] == '\'' && s[i + 1] == '\'') - { - /* doubled quote becomes just one quote */ - newStr[j] = s[++i]; - } - else - newStr[j] = s[i]; - j++; - } - - /* We copied the ending quote to newStr, so replace with \0 */ - Assert(j > 0 && j <= len); - newStr[--j] = '\0'; - - return newStr; -} - - -/* - * Flex fatal errors bring us here. Stash the error message and jump back to - * ParseConfigFp(). Assume all msg arguments point to string constants; this - * holds for flex 2.5.31 (earliest we support) and flex 2.5.35 (latest as of - * this writing). Otherwise, we would need to copy the message. - * - * We return "int" since this takes the place of calls to fprintf(). -*/ -static int -CONF_flex_fatal(const char *msg) -{ - CONF_flex_fatal_errmsg = msg; - siglongjmp(*CONF_flex_fatal_jmp, 1); - return 0; /* keep compiler quiet */ -} diff --git a/configfile.c b/configfile.c index 812ba908..d974895f 100644 --- a/configfile.c +++ b/configfile.c @@ -266,6 +266,12 @@ static void _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *warning_list) { FILE *fp; + char *s = NULL, + buf[MAXLINELENGTH] = ""; + char name[MAXLEN] = ""; + char value[MAXLEN] = ""; + + bool node_id_found = false; /* Initialize configuration options with sensible defaults */ @@ -462,12 +468,365 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * exit(ERR_BAD_CONFIG); } - (void) ProcessConfigFile(fp, config_file_path, options, error_list, warning_list); + /* Read file */ + while ((s = fgets(buf, sizeof buf, fp)) != NULL) + { + bool known_parameter = true; + + /* Parse name/value pair from line */ + _parse_line(buf, name, value); + + /* Skip blank lines */ + if (!strlen(name)) + continue; + + /* Skip comments */ + if (name[0] == '#') + continue; + + /* Copy into correct entry in parameters struct */ + if (strcmp(name, "node_id") == 0) + { + options->node_id = repmgr_atoi(value, name, error_list, MIN_NODE_ID); + node_id_found = true; + } + else if (strcmp(name, "node_name") == 0) + { + if (strlen(value) < sizeof(options->node_name)) + strncpy(options->node_name, value, sizeof(options->node_name)); + else + item_list_append_format(error_list, + _("value for \"node_name\" must contain fewer than %lu characters"), + sizeof(options->node_name)); + } + else if (strcmp(name, "conninfo") == 0) + strncpy(options->conninfo, value, MAXLEN); + else if (strcmp(name, "data_directory") == 0) + { + strncpy(options->data_directory, value, MAXPGPATH); + canonicalize_path(options->data_directory); + } + else if (strcmp(name, "config_directory") == 0) + { + strncpy(options->config_directory, value, MAXPGPATH); + canonicalize_path(options->config_directory); + } + else if (strcmp(name, "replication_user") == 0) + { + if (strlen(value) < sizeof(options->replication_user)) + strncpy(options->replication_user, value, sizeof(options->replication_user)); + else + item_list_append_format(error_list, + _("value for \"replication_user\" must contain fewer than %lu characters"), + sizeof(options->replication_user)); + } + else if (strcmp(name, "pg_bindir") == 0) + strncpy(options->pg_bindir, value, MAXPGPATH); + else if (strcmp(name, "repmgr_bindir") == 0) + strncpy(options->repmgr_bindir, value, MAXPGPATH); + + else if (strcmp(name, "replication_type") == 0) + { + if (strcmp(value, "physical") == 0) + options->replication_type = REPLICATION_TYPE_PHYSICAL; + else if (strcmp(value, "bdr") == 0) + options->replication_type = REPLICATION_TYPE_BDR; + else + item_list_append(error_list, _("value for \"replication_type\" must be \"physical\" or \"bdr\"")); + } + + /* log settings */ + else if (strcmp(name, "log_file") == 0) + strncpy(options->log_file, value, MAXLEN); + else if (strcmp(name, "log_level") == 0) + strncpy(options->log_level, value, MAXLEN); + else if (strcmp(name, "log_facility") == 0) + strncpy(options->log_facility, value, MAXLEN); + else if (strcmp(name, "log_status_interval") == 0) + options->log_status_interval = repmgr_atoi(value, name, error_list, 0); + + /* standby clone settings */ + else if (strcmp(name, "use_replication_slots") == 0) + options->use_replication_slots = parse_bool(value, name, error_list); + else if (strcmp(name, "pg_basebackup_options") == 0) + strncpy(options->pg_basebackup_options, value, MAXLEN); + else if (strcmp(name, "tablespace_mapping") == 0) + tablespace_list_append(options, value); + else if (strcmp(name, "restore_command") == 0) + strncpy(options->restore_command, value, MAXLEN); + else if (strcmp(name, "recovery_min_apply_delay") == 0) + { + parse_time_unit_parameter(name, value, options->recovery_min_apply_delay, error_list); + options->recovery_min_apply_delay_provided = true; + } + else if (strcmp(name, "archive_cleanup_command") == 0) + strncpy(options->archive_cleanup_command, value, MAXLEN); + else if (strcmp(name, "use_primary_conninfo_password") == 0) + options->use_primary_conninfo_password = parse_bool(value, name, error_list); + else if (strcmp(name, "passfile") == 0) + strncpy(options->passfile, value, sizeof(options->passfile)); + + /* standby promote settings */ + else if (strcmp(name, "promote_check_timeout") == 0) + options->promote_check_timeout = repmgr_atoi(value, name, error_list, 1); + + else if (strcmp(name, "promote_check_interval") == 0) + options->promote_check_interval = repmgr_atoi(value, name, error_list, 1); + + /* standby follow settings */ + else if (strcmp(name, "primary_follow_timeout") == 0) + options->primary_follow_timeout = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "standby_follow_timeout") == 0) + options->standby_follow_timeout = repmgr_atoi(value, name, error_list, 0); + + /* standby switchover settings */ + else if (strcmp(name, "shutdown_check_timeout") == 0) + options->shutdown_check_timeout = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "standby_reconnect_timeout") == 0) + options->standby_reconnect_timeout = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "wal_receive_check_timeout") == 0) + options->wal_receive_check_timeout = repmgr_atoi(value, name, error_list, 0); + + /* node rejoin settings */ + else if (strcmp(name, "node_rejoin_timeout") == 0) + options->node_rejoin_timeout = repmgr_atoi(value, name, error_list, 0); + + /* node check settings */ + else if (strcmp(name, "archive_ready_warning") == 0) + options->archive_ready_warning = repmgr_atoi(value, name, error_list, 1); + else if (strcmp(name, "archive_ready_critical") == 0) + options->archive_ready_critical = repmgr_atoi(value, name, error_list, 1); + else if (strcmp(name, "replication_lag_warning") == 0) + options->replication_lag_warning = repmgr_atoi(value, name, error_list, 1); + else if (strcmp(name, "replication_lag_critical") == 0) + options->replication_lag_critical = repmgr_atoi(value, name, error_list, 1); + + /* repmgrd settings */ + else if (strcmp(name, "failover") == 0) + { + if (strcmp(value, "manual") == 0) + { + options->failover = FAILOVER_MANUAL; + } + else if (strcmp(value, "automatic") == 0) + { + options->failover = FAILOVER_AUTOMATIC; + } + else + { + item_list_append(error_list, + _("value for \"failover\" must be \"automatic\" or \"manual\"\n")); + } + } + else if (strcmp(name, "priority") == 0) + options->priority = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "location") == 0) + strncpy(options->location, value, sizeof(options->location)); + else if (strcmp(name, "promote_command") == 0) + strncpy(options->promote_command, value, sizeof(options->promote_command)); + else if (strcmp(name, "follow_command") == 0) + strncpy(options->follow_command, value, sizeof(options->follow_command)); + else if (strcmp(name, "reconnect_attempts") == 0) + options->reconnect_attempts = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "reconnect_interval") == 0) + options->reconnect_interval = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "monitor_interval_secs") == 0) + options->monitor_interval_secs = repmgr_atoi(value, name, error_list, 1); + else if (strcmp(name, "monitoring_history") == 0) + options->monitoring_history = parse_bool(value, name, error_list); + else if (strcmp(name, "degraded_monitoring_timeout") == 0) + options->degraded_monitoring_timeout = repmgr_atoi(value, name, error_list, -1); + else if (strcmp(name, "async_query_timeout") == 0) + options->async_query_timeout = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "primary_notification_timeout") == 0) + options->primary_notification_timeout = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "repmgrd_standby_startup_timeout") == 0) + options->repmgrd_standby_startup_timeout = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "repmgrd_pid_file") == 0) + strncpy(options->repmgrd_pid_file, value, MAXPGPATH); + else if (strcmp(name, "standby_disconnect_on_failover") == 0) + options->standby_disconnect_on_failover = parse_bool(value, name, error_list); + else if (strcmp(name, "sibling_nodes_disconnect_timeout") == 0) + options->sibling_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "connection_check_type") == 0) + { + if (strcasecmp(value, "ping") == 0) + { + options->connection_check_type = CHECK_PING; + } + else if (strcasecmp(value, "connection") == 0) + { + options->connection_check_type = CHECK_CONNECTION; + } + else if (strcasecmp(value, "query") == 0) + { + options->connection_check_type = CHECK_QUERY; + } + else + { + item_list_append(error_list, + _("value for \"connection_check_type\" must be \"ping\", \"connection\" or \"query\"\n")); + } + } + else if (strcmp(name, "primary_visibility_consensus") == 0) + options->primary_visibility_consensus = parse_bool(value, name, error_list); + else if (strcmp(name, "failover_validation_command") == 0) + strncpy(options->failover_validation_command, value, sizeof(options->failover_validation_command)); + else if (strcmp(name, "election_rerun_interval") == 0) + options->election_rerun_interval = repmgr_atoi(value, name, error_list, 0); + else if (strcmp(name, "child_nodes_check_interval") == 0) + options->child_nodes_check_interval = repmgr_atoi(value, name, error_list, 1); + else if (strcmp(name, "child_nodes_disconnect_command") == 0) + snprintf(options->child_nodes_disconnect_command, sizeof(options->child_nodes_disconnect_command), "%s", value); + else if (strcmp(name, "child_nodes_disconnect_min_count") == 0) + options->child_nodes_disconnect_min_count = repmgr_atoi(value, name, error_list, -1); + else if (strcmp(name, "child_nodes_connected_min_count") == 0) + options->child_nodes_connected_min_count = repmgr_atoi(value, name, error_list, -1); + else if (strcmp(name, "child_nodes_connected_include_witness") == 0) + options->child_nodes_connected_include_witness = parse_bool(value, name, error_list); + else if (strcmp(name, "child_nodes_disconnect_timeout") == 0) + options->child_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0); + + /* witness settings */ + else if (strcmp(name, "witness_sync_interval") == 0) + options->witness_sync_interval = repmgr_atoi(value, name, error_list, 1); + + /* BDR settings */ + else if (strcmp(name, "bdr_local_monitoring_only") == 0) + options->bdr_local_monitoring_only = parse_bool(value, name, error_list); + else if (strcmp(name, "bdr_recovery_timeout") == 0) + options->bdr_recovery_timeout = repmgr_atoi(value, name, error_list, 0); + + /* service settings */ + else if (strcmp(name, "pg_ctl_options") == 0) + strncpy(options->pg_ctl_options, value, sizeof(options->pg_ctl_options)); + else if (strcmp(name, "service_start_command") == 0) + strncpy(options->service_start_command, value, sizeof(options->service_start_command)); + else if (strcmp(name, "service_stop_command") == 0) + strncpy(options->service_stop_command, value, sizeof(options->service_stop_command)); + else if (strcmp(name, "service_restart_command") == 0) + strncpy(options->service_restart_command, value, sizeof(options->service_restart_command)); + else if (strcmp(name, "service_reload_command") == 0) + strncpy(options->service_reload_command, value, sizeof(options->service_reload_command)); + else if (strcmp(name, "service_promote_command") == 0) + strncpy(options->service_promote_command, value, sizeof(options->service_promote_command)); + + /* repmgrd service settings */ + else if (strcmp(name, "repmgrd_service_start_command") == 0) + strncpy(options->repmgrd_service_start_command, value, sizeof(options->repmgrd_service_start_command)); + else if (strcmp(name, "repmgrd_service_stop_command") == 0) + strncpy(options->repmgrd_service_stop_command, value, sizeof(options->repmgrd_service_stop_command)); + + + /* event notification settings */ + else if (strcmp(name, "event_notification_command") == 0) + strncpy(options->event_notification_command, value, sizeof(options->event_notification_command)); + else if (strcmp(name, "event_notifications") == 0) + { + /* store unparsed value for comparison when reloading config */ + strncpy(options->event_notifications_orig, value, sizeof(options->event_notifications_orig)); + parse_event_notifications_list(options, value); + } + + /* barman settings */ + else if (strcmp(name, "barman_host") == 0) + strncpy(options->barman_host, value, sizeof(options->barman_host)); + else if (strcmp(name, "barman_server") == 0) + strncpy(options->barman_server, value, sizeof(options->barman_server)); + else if (strcmp(name, "barman_config") == 0) + strncpy(options->barman_config, value, sizeof(options->barman_config)); + + /* rsync/ssh settings */ + else if (strcmp(name, "rsync_options") == 0) + strncpy(options->rsync_options, value, sizeof(options->rsync_options)); + else if (strcmp(name, "ssh_options") == 0) + strncpy(options->ssh_options, value, sizeof(options->ssh_options)); + + /* undocumented settings for testing */ + else if (strcmp(name, "promote_delay") == 0) + options->promote_delay = repmgr_atoi(value, name, error_list, 1); + + /* + * Following parameters have been deprecated or renamed from 3.x - + * issue a warning + */ + else if (strcmp(name, "cluster") == 0) + { + item_list_append(warning_list, + _("parameter \"cluster\" is deprecated and will be ignored")); + known_parameter = false; + } + else if (strcmp(name, "node") == 0) + { + item_list_append(warning_list, + _("parameter \"node\" has been renamed to \"node_id\"")); + known_parameter = false; + } + else if (strcmp(name, "upstream_node") == 0) + { + item_list_append(warning_list, + _("parameter \"upstream_node\" has been removed; use \"--upstream-node-id\" when cloning a standby")); + known_parameter = false; + } + else if (strcmp(name, "loglevel") == 0) + { + item_list_append(warning_list, + _("parameter \"loglevel\" has been renamed to \"log_level\"")); + known_parameter = false; + } + else if (strcmp(name, "logfacility") == 0) + { + item_list_append(warning_list, + _("parameter \"logfacility\" has been renamed to \"log_facility\"")); + known_parameter = false; + } + else if (strcmp(name, "logfile") == 0) + { + item_list_append(warning_list, + _("parameter \"logfile\" has been renamed to \"log_file\"")); + known_parameter = false; + } + else if (strcmp(name, "master_reponse_timeout") == 0) + { + item_list_append(warning_list, + _("parameter \"master_reponse_timeout\" has been removed; use \"async_query_timeout\" instead")); + known_parameter = false; + } + else if (strcmp(name, "retry_promote_interval_secs") == 0) + { + item_list_append(warning_list, + _("parameter \"retry_promote_interval_secs\" has been removed; use \"primary_notification_timeout\" instead")); + known_parameter = false; + } + else + { + known_parameter = false; + log_warning(_("%s/%s: unknown name/value pair provided; ignoring"), name, value); + } + + /* + * Raise an error if a known parameter is provided with an empty + * value. Currently there's no reason why empty parameters are needed; + * if we want to accept those, we'd need to add stricter default + * checking, as currently e.g. an empty `node_id` value will be converted + * to '0'. + */ + if (known_parameter == true && !strlen(value)) + { + char error_message_buf[MAXLEN] = ""; + + maxlen_snprintf(error_message_buf, + _("\"%s\": no value provided"), + name); + + item_list_append(error_list, error_message_buf); + } + } fclose(fp); /* check required parameters */ - if (options->node_id == UNKNOWN_NODE_ID) + if (node_id_found == false) { item_list_append(error_list, _("\"node_id\": required parameter was not found")); } @@ -560,349 +919,6 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * } -void -parse_configuration_item(t_configuration_options *options, ItemList *error_list, ItemList *warning_list, const char *name, const char *value) -{ - bool known_parameter = true; - - if (strcmp(name, "node_id") == 0) - { - options->node_id = repmgr_atoi(value, name, error_list, MIN_NODE_ID); - } - else if (strcmp(name, "node_name") == 0) - { - if (strlen(value) < sizeof(options->node_name)) - strncpy(options->node_name, value, sizeof(options->node_name)); - else - item_list_append_format(error_list, - _("value for \"node_name\" must contain fewer than %lu characters"), - sizeof(options->node_name)); - } - else if (strcmp(name, "conninfo") == 0) - { - strncpy(options->conninfo, value, MAXLEN); - } - else if (strcmp(name, "data_directory") == 0) - { - strncpy(options->data_directory, value, MAXPGPATH); - canonicalize_path(options->data_directory); - } - else if (strcmp(name, "config_directory") == 0) - { - strncpy(options->config_directory, value, MAXPGPATH); - canonicalize_path(options->config_directory); - } - else if (strcmp(name, "replication_user") == 0) - { - if (strlen(value) < sizeof(options->replication_user)) - strncpy(options->replication_user, value, sizeof(options->replication_user)); - else - item_list_append_format(error_list, - _("value for \"replication_user\" must contain fewer than %lu characters"), - sizeof(options->replication_user)); - } - else if (strcmp(name, "pg_bindir") == 0) - strncpy(options->pg_bindir, value, MAXPGPATH); - else if (strcmp(name, "repmgr_bindir") == 0) - strncpy(options->repmgr_bindir, value, MAXPGPATH); - - else if (strcmp(name, "replication_type") == 0) - { - if (strcmp(value, "physical") == 0) - options->replication_type = REPLICATION_TYPE_PHYSICAL; - else if (strcmp(value, "bdr") == 0) - options->replication_type = REPLICATION_TYPE_BDR; - else - item_list_append(error_list, _("value for \"replication_type\" must be \"physical\" or \"bdr\"")); - } - - /* log settings */ - else if (strcmp(name, "log_file") == 0) - strncpy(options->log_file, value, MAXLEN); - else if (strcmp(name, "log_level") == 0) - strncpy(options->log_level, value, MAXLEN); - else if (strcmp(name, "log_facility") == 0) - strncpy(options->log_facility, value, MAXLEN); - else if (strcmp(name, "log_status_interval") == 0) - options->log_status_interval = repmgr_atoi(value, name, error_list, 0); - - /* standby clone settings */ - else if (strcmp(name, "use_replication_slots") == 0) - options->use_replication_slots = parse_bool(value, name, error_list); - else if (strcmp(name, "pg_basebackup_options") == 0) - strncpy(options->pg_basebackup_options, value, MAXLEN); - else if (strcmp(name, "tablespace_mapping") == 0) - tablespace_list_append(options, value); - else if (strcmp(name, "restore_command") == 0) - strncpy(options->restore_command, value, MAXLEN); - else if (strcmp(name, "recovery_min_apply_delay") == 0) - { - parse_time_unit_parameter(name, value, options->recovery_min_apply_delay, error_list); - options->recovery_min_apply_delay_provided = true; - } - else if (strcmp(name, "archive_cleanup_command") == 0) - strncpy(options->archive_cleanup_command, value, MAXLEN); - else if (strcmp(name, "use_primary_conninfo_password") == 0) - options->use_primary_conninfo_password = parse_bool(value, name, error_list); - else if (strcmp(name, "passfile") == 0) - strncpy(options->passfile, value, sizeof(options->passfile)); - - /* standby promote settings */ - else if (strcmp(name, "promote_check_timeout") == 0) - options->promote_check_timeout = repmgr_atoi(value, name, error_list, 1); - - else if (strcmp(name, "promote_check_interval") == 0) - options->promote_check_interval = repmgr_atoi(value, name, error_list, 1); - - /* standby follow settings */ - else if (strcmp(name, "primary_follow_timeout") == 0) - options->primary_follow_timeout = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "standby_follow_timeout") == 0) - options->standby_follow_timeout = repmgr_atoi(value, name, error_list, 0); - - /* standby switchover settings */ - else if (strcmp(name, "shutdown_check_timeout") == 0) - options->shutdown_check_timeout = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "standby_reconnect_timeout") == 0) - options->standby_reconnect_timeout = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "wal_receive_check_timeout") == 0) - options->wal_receive_check_timeout = repmgr_atoi(value, name, error_list, 0); - - /* node rejoin settings */ - else if (strcmp(name, "node_rejoin_timeout") == 0) - options->node_rejoin_timeout = repmgr_atoi(value, name, error_list, 0); - - /* node check settings */ - else if (strcmp(name, "archive_ready_warning") == 0) - options->archive_ready_warning = repmgr_atoi(value, name, error_list, 1); - else if (strcmp(name, "archive_ready_critical") == 0) - options->archive_ready_critical = repmgr_atoi(value, name, error_list, 1); - else if (strcmp(name, "replication_lag_warning") == 0) - options->replication_lag_warning = repmgr_atoi(value, name, error_list, 1); - else if (strcmp(name, "replication_lag_critical") == 0) - options->replication_lag_critical = repmgr_atoi(value, name, error_list, 1); - - /* repmgrd settings */ - else if (strcmp(name, "failover") == 0) - { - if (strcmp(value, "manual") == 0) - { - options->failover = FAILOVER_MANUAL; - } - else if (strcmp(value, "automatic") == 0) - { - options->failover = FAILOVER_AUTOMATIC; - } - else - { - item_list_append(error_list, - _("value for \"failover\" must be \"automatic\" or \"manual\"\n")); - } - } - else if (strcmp(name, "priority") == 0) - options->priority = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "location") == 0) - strncpy(options->location, value, sizeof(options->location)); - else if (strcmp(name, "promote_command") == 0) - strncpy(options->promote_command, value, sizeof(options->promote_command)); - else if (strcmp(name, "follow_command") == 0) - strncpy(options->follow_command, value, sizeof(options->follow_command)); - else if (strcmp(name, "reconnect_attempts") == 0) - options->reconnect_attempts = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "reconnect_interval") == 0) - options->reconnect_interval = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "monitor_interval_secs") == 0) - options->monitor_interval_secs = repmgr_atoi(value, name, error_list, 1); - else if (strcmp(name, "monitoring_history") == 0) - options->monitoring_history = parse_bool(value, name, error_list); - else if (strcmp(name, "degraded_monitoring_timeout") == 0) - options->degraded_monitoring_timeout = repmgr_atoi(value, name, error_list, -1); - else if (strcmp(name, "async_query_timeout") == 0) - options->async_query_timeout = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "primary_notification_timeout") == 0) - options->primary_notification_timeout = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "repmgrd_standby_startup_timeout") == 0) - options->repmgrd_standby_startup_timeout = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "repmgrd_pid_file") == 0) - strncpy(options->repmgrd_pid_file, value, MAXPGPATH); - else if (strcmp(name, "standby_disconnect_on_failover") == 0) - options->standby_disconnect_on_failover = parse_bool(value, name, error_list); - else if (strcmp(name, "sibling_nodes_disconnect_timeout") == 0) - options->sibling_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "connection_check_type") == 0) - { - if (strcasecmp(value, "ping") == 0) - { - options->connection_check_type = CHECK_PING; - } - else if (strcasecmp(value, "connection") == 0) - { - options->connection_check_type = CHECK_CONNECTION; - } - else if (strcasecmp(value, "query") == 0) - { - options->connection_check_type = CHECK_QUERY; - } - else - { - item_list_append(error_list, - _("value for \"connection_check_type\" must be \"ping\", \"connection\" or \"query\"\n")); - } - } - else if (strcmp(name, "primary_visibility_consensus") == 0) - options->primary_visibility_consensus = parse_bool(value, name, error_list); - else if (strcmp(name, "failover_validation_command") == 0) - strncpy(options->failover_validation_command, value, sizeof(options->failover_validation_command)); - else if (strcmp(name, "election_rerun_interval") == 0) - options->election_rerun_interval = repmgr_atoi(value, name, error_list, 0); - else if (strcmp(name, "child_nodes_check_interval") == 0) - options->child_nodes_check_interval = repmgr_atoi(value, name, error_list, 1); - else if (strcmp(name, "child_nodes_disconnect_command") == 0) - snprintf(options->child_nodes_disconnect_command, sizeof(options->child_nodes_disconnect_command), "%s", value); - else if (strcmp(name, "child_nodes_disconnect_min_count") == 0) - options->child_nodes_disconnect_min_count = repmgr_atoi(value, name, error_list, -1); - else if (strcmp(name, "child_nodes_connected_min_count") == 0) - options->child_nodes_connected_min_count = repmgr_atoi(value, name, error_list, -1); - else if (strcmp(name, "child_nodes_connected_include_witness") == 0) - options->child_nodes_connected_include_witness = parse_bool(value, name, error_list); - else if (strcmp(name, "child_nodes_disconnect_timeout") == 0) - options->child_nodes_disconnect_timeout = repmgr_atoi(value, name, error_list, 0); - - /* witness settings */ - else if (strcmp(name, "witness_sync_interval") == 0) - options->witness_sync_interval = repmgr_atoi(value, name, error_list, 1); - - /* BDR settings */ - else if (strcmp(name, "bdr_local_monitoring_only") == 0) - options->bdr_local_monitoring_only = parse_bool(value, name, error_list); - else if (strcmp(name, "bdr_recovery_timeout") == 0) - options->bdr_recovery_timeout = repmgr_atoi(value, name, error_list, 0); - - /* service settings */ - else if (strcmp(name, "pg_ctl_options") == 0) - strncpy(options->pg_ctl_options, value, sizeof(options->pg_ctl_options)); - else if (strcmp(name, "service_start_command") == 0) - strncpy(options->service_start_command, value, sizeof(options->service_start_command)); - else if (strcmp(name, "service_stop_command") == 0) - strncpy(options->service_stop_command, value, sizeof(options->service_stop_command)); - else if (strcmp(name, "service_restart_command") == 0) - strncpy(options->service_restart_command, value, sizeof(options->service_restart_command)); - else if (strcmp(name, "service_reload_command") == 0) - strncpy(options->service_reload_command, value, sizeof(options->service_reload_command)); - else if (strcmp(name, "service_promote_command") == 0) - strncpy(options->service_promote_command, value, sizeof(options->service_promote_command)); - - /* repmgrd service settings */ - else if (strcmp(name, "repmgrd_service_start_command") == 0) - strncpy(options->repmgrd_service_start_command, value, sizeof(options->repmgrd_service_start_command)); - else if (strcmp(name, "repmgrd_service_stop_command") == 0) - strncpy(options->repmgrd_service_stop_command, value, sizeof(options->repmgrd_service_stop_command)); - - - /* event notification settings */ - else if (strcmp(name, "event_notification_command") == 0) - strncpy(options->event_notification_command, value, sizeof(options->event_notification_command)); - else if (strcmp(name, "event_notifications") == 0) - { - /* store unparsed value for comparison when reloading config */ - strncpy(options->event_notifications_orig, value, sizeof(options->event_notifications_orig)); - parse_event_notifications_list(options, value); - } - - /* barman settings */ - else if (strcmp(name, "barman_host") == 0) - strncpy(options->barman_host, value, sizeof(options->barman_host)); - else if (strcmp(name, "barman_server") == 0) - strncpy(options->barman_server, value, sizeof(options->barman_server)); - else if (strcmp(name, "barman_config") == 0) - strncpy(options->barman_config, value, sizeof(options->barman_config)); - - /* rsync/ssh settings */ - else if (strcmp(name, "rsync_options") == 0) - strncpy(options->rsync_options, value, sizeof(options->rsync_options)); - else if (strcmp(name, "ssh_options") == 0) - strncpy(options->ssh_options, value, sizeof(options->ssh_options)); - - /* undocumented settings for testing */ - else if (strcmp(name, "promote_delay") == 0) - options->promote_delay = repmgr_atoi(value, name, error_list, 1); - - /* - * Following parameters have been deprecated or renamed from 3.x - - * issue a warning - */ - else if (strcmp(name, "cluster") == 0) - { - item_list_append(warning_list, - _("parameter \"cluster\" is deprecated and will be ignored")); - known_parameter = false; - } - else if (strcmp(name, "node") == 0) - { - item_list_append(warning_list, - _("parameter \"node\" has been renamed to \"node_id\"")); - known_parameter = false; - } - else if (strcmp(name, "upstream_node") == 0) - { - item_list_append(warning_list, - _("parameter \"upstream_node\" has been removed; use \"--upstream-node-id\" when cloning a standby")); - known_parameter = false; - } - else if (strcmp(name, "loglevel") == 0) - { - item_list_append(warning_list, - _("parameter \"loglevel\" has been renamed to \"log_level\"")); - known_parameter = false; - } - else if (strcmp(name, "logfacility") == 0) - { - item_list_append(warning_list, - _("parameter \"logfacility\" has been renamed to \"log_facility\"")); - known_parameter = false; - } - else if (strcmp(name, "logfile") == 0) - { - item_list_append(warning_list, - _("parameter \"logfile\" has been renamed to \"log_file\"")); - known_parameter = false; - } - else if (strcmp(name, "master_reponse_timeout") == 0) - { - item_list_append(warning_list, - _("parameter \"master_reponse_timeout\" has been removed; use \"async_query_timeout\" instead")); - known_parameter = false; - } - else if (strcmp(name, "retry_promote_interval_secs") == 0) - { - item_list_append(warning_list, - _("parameter \"retry_promote_interval_secs\" has been removed; use \"primary_notification_timeout\" instead")); - known_parameter = false; - } - else - { - known_parameter = false; - log_warning(_("%s/%s: unknown name/value pair provided; ignoring"), name, value); - } - - /* - * Raise an error if a known parameter is provided with an empty - * value. Currently there's no reason why empty parameters are needed; - * if we want to accept those, we'd need to add stricter default - * checking, as currently e.g. an empty `node_id` value will be converted - * to '0'. - */ - if (known_parameter == true && !strlen(value)) - { - char error_message_buf[MAXLEN] = ""; - - maxlen_snprintf(error_message_buf, - _("\"%s\": no value provided"), - name); - - item_list_append(error_list, error_message_buf); - } -} bool diff --git a/configfile.h b/configfile.h index f446b8db..5759de7d 100644 --- a/configfile.h +++ b/configfile.h @@ -317,8 +317,6 @@ const char *progname(void); void load_config(const char *config_file, bool verbose, bool terse, t_configuration_options *options, char *argv0); bool reload_config(t_configuration_options *orig_options, t_server_type server_type); -void parse_configuration_item(t_configuration_options *options, ItemList *error_list, ItemList *warning_list, const char *name, const char *value); - bool parse_recovery_conf(const char *data_dir, t_recovery_conf *conf); bool parse_bool(const char *s, @@ -344,7 +342,4 @@ void exit_with_cli_errors(ItemList *error_list, const char *repmgr_command); void print_item_list(ItemList *item_list); const char *print_connection_check_type(ConnectionCheckType type); - -extern bool ProcessConfigFile(FILE *fp, const char *config_file, t_configuration_options *options, ItemList *error_list, ItemList *warning_list); - #endif /* _REPMGR_CONFIGFILE_H_ */ diff --git a/doc/appendix-release-notes.xml b/doc/appendix-release-notes.xml index 0ed1737a..7ba5284f 100644 --- a/doc/appendix-release-notes.xml +++ b/doc/appendix-release-notes.xml @@ -24,33 +24,6 @@ General enhancements - - - - - The &repmgr; configuration file is now parsed using - flex, meaning it will be parsed in - the same way as PostgreSQL parses its own configuration - files. - - - This makes configuration file parsing more robust - and consistent. - - - - This change makes configuration file parsing somewhat stricter - than previously. When upgrading, be sure to check your - configuration file syntax. - - - - In particular, all string values containing spaces - must be contained within single quotes. - - - -