diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index 71c9adf6..f0160d50 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -96,6 +96,11 @@ static void get_barman_property(char *dst, char *name, char *local_repmgr_direct static int get_tablespace_data_barman(char *, TablespaceDataList *); static char *make_barman_ssh_command(char *buf); +static bool create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir); +static void write_primary_conninfo(char *line, t_conninfo_param_list *param_list); +static bool write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line); + + static NodeStatus parse_node_status_is_shutdown_cleanly(const char *node_status_output, XLogRecPtr *checkPoint); static CheckStatus parse_node_check_archiver(const char *node_check_output, int *files, int *threshold); static CheckStatus parse_node_check_replication_lag(const char *node_check_output, int *seconds, int *threshold); @@ -4751,6 +4756,225 @@ drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name) } +/* + * Creates a recovery.conf file for a standby + * + * A database connection pointer is required for escaping primary_conninfo + * parameters. When cloning from Barman and --no-upstream-connection ) this + * might not be available. + */ +bool +create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir) +{ + FILE *recovery_file; + char recovery_file_path[MAXPGPATH] = ""; + char line[MAXLEN] = ""; + mode_t um; + + maxpath_snprintf(recovery_file_path, "%s/%s", data_dir, RECOVERY_COMMAND_FILE); + + /* Set umask to 0600 */ + um = umask((~(S_IRUSR | S_IWUSR)) & (S_IRWXG | S_IRWXO)); + recovery_file = fopen(recovery_file_path, "w"); + umask(um); + + if (recovery_file == NULL) + { + log_error(_("unable to create recovery.conf file at \"%s\""), + recovery_file_path); + log_detail("%s", strerror(errno)); + + return false; + } + + log_debug("create_recovery_file(): creating \"%s\"...", + recovery_file_path); + + /* standby_mode = 'on' */ + maxlen_snprintf(line, "standby_mode = 'on'\n"); + + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + + /* primary_conninfo = '...' */ + + /* + * the user specified --upstream-conninfo string - copy that + */ + if (strlen(runtime_options.upstream_conninfo)) + { + char *escaped = escape_recovery_conf_value(runtime_options.upstream_conninfo); + + maxlen_snprintf(line, "primary_conninfo = '%s'\n", + escaped); + free(escaped); + } + + /* + * otherwise use the conninfo inferred from the upstream connection and/or + * node record + */ + else + { + write_primary_conninfo(line, recovery_conninfo); + } + + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + + /* recovery_target_timeline = 'latest' */ + maxlen_snprintf(line, "recovery_target_timeline = 'latest'\n"); + + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + + /* recovery_min_apply_delay = ... (optional) */ + if (*config_file_options.recovery_min_apply_delay) + { + maxlen_snprintf(line, "recovery_min_apply_delay = %s\n", + config_file_options.recovery_min_apply_delay); + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + } + + /* primary_slot_name = '...' (optional, for 9.4 and later) */ + if (config_file_options.use_replication_slots) + { + maxlen_snprintf(line, "primary_slot_name = %s\n", + node_record->slot_name); + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + } + + /* + * If restore_command is set, we use it as restore_command in + * recovery.conf + */ + if (strcmp(config_file_options.restore_command, "") != 0) + { + maxlen_snprintf(line, "restore_command = '%s'\n", + config_file_options.restore_command); + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + } + fclose(recovery_file); + + return true; +} + + +static bool +write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line) +{ + if (fputs(line, recovery_file) == EOF) + { + log_error(_("unable to write to recovery file at \"%s\""), recovery_file_path); + fclose(recovery_file); + return false; + } + + return true; +} + + +static void +write_primary_conninfo(char *line, t_conninfo_param_list *param_list) +{ + PQExpBufferData conninfo_buf; + bool application_name_provided = false; + bool password_provided = false; + int c; + char *escaped = NULL; + t_conninfo_param_list env_conninfo; + + initialize_conninfo_params(&env_conninfo, true); + + initPQExpBuffer(&conninfo_buf); + + for (c = 0; c < param_list->size && param_list->keywords[c] != NULL; c++) + { + /* + * Skip empty settings and ones which don't make any sense in + * recovery.conf + */ + if (strcmp(param_list->keywords[c], "dbname") == 0 || + strcmp(param_list->keywords[c], "replication") == 0 || + (param_list->values[c] == NULL) || + (param_list->values[c] != NULL && param_list->values[c][0] == '\0')) + continue; + + /* only include "password" if explicitly requested */ + if (strcmp(param_list->keywords[c], "password") == 0) + { + password_provided = true; + } + + if (conninfo_buf.len != 0) + appendPQExpBufferChar(&conninfo_buf, ' '); + + if (strcmp(param_list->keywords[c], "application_name") == 0) + application_name_provided = true; + + appendPQExpBuffer(&conninfo_buf, "%s=", param_list->keywords[c]); + appendConnStrVal(&conninfo_buf, param_list->values[c]); + } + + /* "application_name" not provided - default to repmgr node name */ + if (application_name_provided == false) + { + if (strlen(config_file_options.node_name)) + { + appendPQExpBuffer(&conninfo_buf, " application_name="); + appendConnStrVal(&conninfo_buf, config_file_options.node_name); + } + else + { + appendPQExpBuffer(&conninfo_buf, " application_name=repmgr"); + } + } + + /* no password provided explicitly */ + if (password_provided == false) + { + if (config_file_options.use_primary_conninfo_password == true) + { + const char *password = param_get(&env_conninfo, "password"); + + if (password != NULL) + { + appendPQExpBuffer(&conninfo_buf, " password="); + appendConnStrVal(&conninfo_buf, password); + } + } + } + + escaped = escape_recovery_conf_value(conninfo_buf.data); + maxlen_snprintf(line, "primary_conninfo = '%s'\n", escaped); + + free(escaped); + free_conninfo_params(&env_conninfo); + termPQExpBuffer(&conninfo_buf); +} + + /* TODO: consolidate code in below functions */ static NodeStatus parse_node_status_is_shutdown_cleanly(const char *node_status_output, XLogRecPtr *checkPoint) diff --git a/repmgr-client-global.h b/repmgr-client-global.h index 066dbec4..334881ed 100644 --- a/repmgr-client-global.h +++ b/repmgr-client-global.h @@ -211,8 +211,6 @@ extern void print_error_list(ItemList *error_list, int log_level); extern char *make_pg_path(const char *file); -extern bool create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir); - extern void get_superuser_connection(PGconn **conn, PGconn **superuser_conn, PGconn **privileged_conn); extern bool remote_command(const char *host, const char *user, const char *command, PQExpBufferData *outputbuf); diff --git a/repmgr-client.c b/repmgr-client.c index 0494ec51..f1a27e14 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -2272,223 +2272,6 @@ copy_remote_files(char *host, char *remote_user, char *remote_path, -/* - * Creates a recovery.conf file for a standby - * - * A database connection pointer is required for escaping primary_conninfo - * parameters. When cloning from Barman and --no-upstream-connection ) this - * might not be available. - */ -bool -create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir) -{ - FILE *recovery_file; - char recovery_file_path[MAXPGPATH] = ""; - char line[MAXLEN] = ""; - mode_t um; - - maxpath_snprintf(recovery_file_path, "%s/%s", data_dir, RECOVERY_COMMAND_FILE); - - /* Set umask to 0600 */ - um = umask((~(S_IRUSR | S_IWUSR)) & (S_IRWXG | S_IRWXO)); - recovery_file = fopen(recovery_file_path, "w"); - umask(um); - - if (recovery_file == NULL) - { - log_error(_("unable to create recovery.conf file at \"%s\""), - recovery_file_path); - log_detail("%s", strerror(errno)); - - return false; - } - - log_debug("create_recovery_file(): creating \"%s\"...", - recovery_file_path); - - /* standby_mode = 'on' */ - maxlen_snprintf(line, "standby_mode = 'on'\n"); - - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - - /* primary_conninfo = '...' */ - - /* - * the user specified --upstream-conninfo string - copy that - */ - if (strlen(runtime_options.upstream_conninfo)) - { - char *escaped = escape_recovery_conf_value(runtime_options.upstream_conninfo); - - maxlen_snprintf(line, "primary_conninfo = '%s'\n", - escaped); - free(escaped); - } - - /* - * otherwise use the conninfo inferred from the upstream connection and/or - * node record - */ - else - { - write_primary_conninfo(line, recovery_conninfo); - } - - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - - /* recovery_target_timeline = 'latest' */ - maxlen_snprintf(line, "recovery_target_timeline = 'latest'\n"); - - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - - /* recovery_min_apply_delay = ... (optional) */ - if (*config_file_options.recovery_min_apply_delay) - { - maxlen_snprintf(line, "recovery_min_apply_delay = %s\n", - config_file_options.recovery_min_apply_delay); - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - } - - /* primary_slot_name = '...' (optional, for 9.4 and later) */ - if (config_file_options.use_replication_slots) - { - maxlen_snprintf(line, "primary_slot_name = %s\n", - node_record->slot_name); - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - } - - /* - * If restore_command is set, we use it as restore_command in - * recovery.conf - */ - if (strcmp(config_file_options.restore_command, "") != 0) - { - maxlen_snprintf(line, "restore_command = '%s'\n", - config_file_options.restore_command); - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - } - fclose(recovery_file); - - return true; -} - - -static bool -write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line) -{ - if (fputs(line, recovery_file) == EOF) - { - log_error(_("unable to write to recovery file at \"%s\""), recovery_file_path); - fclose(recovery_file); - return false; - } - - return true; -} - - -static void -write_primary_conninfo(char *line, t_conninfo_param_list *param_list) -{ - PQExpBufferData conninfo_buf; - bool application_name_provided = false; - bool password_provided = false; - int c; - char *escaped = NULL; - t_conninfo_param_list env_conninfo; - - initialize_conninfo_params(&env_conninfo, true); - - initPQExpBuffer(&conninfo_buf); - - for (c = 0; c < param_list->size && param_list->keywords[c] != NULL; c++) - { - /* - * Skip empty settings and ones which don't make any sense in - * recovery.conf - */ - if (strcmp(param_list->keywords[c], "dbname") == 0 || - strcmp(param_list->keywords[c], "replication") == 0 || - (param_list->values[c] == NULL) || - (param_list->values[c] != NULL && param_list->values[c][0] == '\0')) - continue; - - /* only include "password" if explicitly requested */ - if (strcmp(param_list->keywords[c], "password") == 0) - { - password_provided = true; - } - - if (conninfo_buf.len != 0) - appendPQExpBufferChar(&conninfo_buf, ' '); - - if (strcmp(param_list->keywords[c], "application_name") == 0) - application_name_provided = true; - - appendPQExpBuffer(&conninfo_buf, "%s=", param_list->keywords[c]); - appendConnStrVal(&conninfo_buf, param_list->values[c]); - } - - /* "application_name" not provided - default to repmgr node name */ - if (application_name_provided == false) - { - if (strlen(config_file_options.node_name)) - { - appendPQExpBuffer(&conninfo_buf, " application_name="); - appendConnStrVal(&conninfo_buf, config_file_options.node_name); - } - else - { - appendPQExpBuffer(&conninfo_buf, " application_name=repmgr"); - } - } - - /* no password provided explicitly */ - if (password_provided == false) - { - if (config_file_options.use_primary_conninfo_password == true) - { - const char *password = param_get(&env_conninfo, "password"); - - if (password != NULL) - { - appendPQExpBuffer(&conninfo_buf, " password="); - appendConnStrVal(&conninfo_buf, password); - } - } - } - - escaped = escape_recovery_conf_value(conninfo_buf.data); - maxlen_snprintf(line, "primary_conninfo = '%s'\n", escaped); - - free(escaped); - free_conninfo_params(&env_conninfo); - termPQExpBuffer(&conninfo_buf); -} /* diff --git a/repmgr-client.h b/repmgr-client.h index f53b1efc..aeeef397 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -204,7 +204,4 @@ static const char *action_name(const int action); static void check_cli_parameters(const int action); -static void write_primary_conninfo(char *line, t_conninfo_param_list *param_list); -static bool write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line); - #endif /* _REPMGR_CLIENT_H_ */