mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-22 22:56:29 +00:00
"standby clone": basic port complete
This commit is contained in:
13
config.c
13
config.c
@@ -96,7 +96,7 @@ load_config(const char *config_file, bool verbose, bool terse, t_configuration_o
|
||||
log_notice(_("looking for configuration file in current directory"));
|
||||
}
|
||||
|
||||
snprintf(config_file_path, MAXPGPATH, "./%s", CONFIG_FILE_NAME);
|
||||
maxpath_snprintf(config_file_path, "./%s", CONFIG_FILE_NAME);
|
||||
canonicalize_path(config_file_path);
|
||||
|
||||
if (stat(config_file_path, &stat_config) == 0)
|
||||
@@ -111,7 +111,7 @@ load_config(const char *config_file, bool verbose, bool terse, t_configuration_o
|
||||
log_notice(_("looking for configuration file in /etc"));
|
||||
}
|
||||
|
||||
snprintf(config_file_path, MAXPGPATH, "/etc/%s", CONFIG_FILE_NAME);
|
||||
maxpath_snprintf(config_file_path, "/etc/%s", CONFIG_FILE_NAME);
|
||||
if (stat(config_file_path, &stat_config) == 0)
|
||||
{
|
||||
config_file_found = true;
|
||||
@@ -132,7 +132,7 @@ load_config(const char *config_file, bool verbose, bool terse, t_configuration_o
|
||||
log_notice(_("looking for configuration file in %s"), sysconf_etc_path);
|
||||
}
|
||||
|
||||
snprintf(config_file_path, MAXPGPATH, "%s/%s", sysconf_etc_path, CONFIG_FILE_NAME);
|
||||
maxpath_snprintf(config_file_path, "%s/%s", sysconf_etc_path, CONFIG_FILE_NAME);
|
||||
if (stat(config_file_path, &stat_config) == 0)
|
||||
{
|
||||
config_file_found = true;
|
||||
@@ -486,10 +486,9 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *
|
||||
*/
|
||||
if (known_parameter == true && !strlen(value)) {
|
||||
char error_message_buf[MAXLEN] = "";
|
||||
snprintf(error_message_buf,
|
||||
MAXLEN,
|
||||
_("\"%s\": no value provided"),
|
||||
name);
|
||||
maxlen_snprintf(error_message_buf,
|
||||
_("\"%s\": no value provided"),
|
||||
name);
|
||||
|
||||
item_list_append(error_list, error_message_buf);
|
||||
}
|
||||
|
||||
38
dbutils.c
38
dbutils.c
@@ -1633,6 +1633,41 @@ create_replication_slot(PGconn *conn, char *slot_name, int server_version_num, P
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
drop_replication_slot(PGconn *conn, char *slot_name)
|
||||
{
|
||||
PQExpBufferData query;
|
||||
PGresult *res;
|
||||
|
||||
initPQExpBuffer(&query);
|
||||
|
||||
appendPQExpBuffer(&query,
|
||||
"SELECT pg_drop_replication_slot('%s')",
|
||||
slot_name);
|
||||
|
||||
log_verbose(LOG_DEBUG, "drop_replication_slot():\n %s", query.data);
|
||||
|
||||
res = PQexec(conn, query.data);
|
||||
|
||||
termPQExpBuffer(&query);
|
||||
|
||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
log_error(_("unable to drop replication slot \"%s\":\n %s"),
|
||||
slot_name,
|
||||
PQerrorMessage(conn));
|
||||
PQclear(res);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_verbose(LOG_DEBUG, "replication slot \"%s\" successfully dropped",
|
||||
slot_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record)
|
||||
{
|
||||
@@ -1650,6 +1685,9 @@ get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record)
|
||||
log_verbose(LOG_DEBUG, "get_slot_record():\n%s", query.data);
|
||||
|
||||
res = PQexec(conn, query.data);
|
||||
|
||||
termPQExpBuffer(&query);
|
||||
|
||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||
{
|
||||
log_error(_("unable to query pg_replication_slots:\n %s"),
|
||||
|
||||
@@ -181,7 +181,7 @@ bool create_event_record_extended(PGconn *conn, t_configuration_options *
|
||||
|
||||
/* replication slot functions */
|
||||
bool create_replication_slot(PGconn *conn, char *slot_name, int server_version_num, PQExpBufferData *error_msg);
|
||||
|
||||
bool drop_replication_slot(PGconn *conn, char *slot_name);
|
||||
int get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record);
|
||||
|
||||
/* backup functions */
|
||||
|
||||
@@ -69,6 +69,8 @@ static char *last_wal_segment = NULL;
|
||||
|
||||
static bool pg_start_backup_executed = false;
|
||||
|
||||
static struct BackupLabel backup_label;
|
||||
|
||||
/* used by barman mode */
|
||||
static char local_repmgr_tmp_directory[MAXPGPATH];
|
||||
static char barman_command_buf[MAXLEN] = "";
|
||||
@@ -82,6 +84,8 @@ static void check_source_server_via_barman(void);
|
||||
static void initialise_direct_clone(void);
|
||||
static void config_file_list_init(t_configfile_list *list, int max_size);
|
||||
static void config_file_list_add(t_configfile_list *list, const char *file, const char *filename, bool in_data_dir);
|
||||
static void copy_configuration_files(void);
|
||||
static void cleanup_data_directory(void);
|
||||
|
||||
static int run_basebackup(void);
|
||||
static int run_file_backup(void);
|
||||
@@ -306,27 +310,64 @@ do_standby_clone(void)
|
||||
if (mode == pg_basebackup)
|
||||
{
|
||||
r = run_basebackup();
|
||||
if (r != 0)
|
||||
{
|
||||
log_warning(_("standby clone: base backup failed"));
|
||||
|
||||
r = ERR_BAD_BASEBACKUP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r = run_file_backup();
|
||||
if (r != 0)
|
||||
{
|
||||
log_warning(_("standby clone: base backup failed"));
|
||||
}
|
||||
|
||||
r = ERR_BAD_BASEBACKUP;
|
||||
|
||||
/* If the backup failed then exit */
|
||||
if (r != 0)
|
||||
{
|
||||
/* If a replication slot was previously created, drop it */
|
||||
if (config_file_options.use_replication_slots)
|
||||
{
|
||||
drop_replication_slot(source_conn, repmgr_slot_name);
|
||||
}
|
||||
|
||||
log_error(_("unable to take a base backup of the master server"));
|
||||
log_warning(_("data directory (%s) may need to be cleaned up manually"),
|
||||
local_data_directory);
|
||||
|
||||
PQfinish(source_conn);
|
||||
exit(r);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If `--copy-external-config-files` was provided, copy any configuration
|
||||
* files detected to the appropriate location. Any errors encountered
|
||||
* will not be treated as fatal.
|
||||
*
|
||||
* XXX check this won't run in Barman mode
|
||||
*/
|
||||
if (runtime_options.copy_external_config_files && config_files.entries)
|
||||
{
|
||||
copy_configuration_files();
|
||||
}
|
||||
|
||||
/* Write the recovery.conf file */
|
||||
|
||||
create_recovery_file(local_data_directory, &recovery_conninfo);
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case rsync:
|
||||
log_notice(_("standby clone (using rsync) complete"));
|
||||
break;
|
||||
|
||||
case pg_basebackup:
|
||||
log_notice(_("standby clone (using pg_basebackup) complete"));
|
||||
break;
|
||||
|
||||
case barman:
|
||||
log_notice(_("standby clone (from Barman) complete"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
check_barman_config(void)
|
||||
{
|
||||
@@ -678,7 +719,7 @@ initialise_direct_clone(void)
|
||||
|
||||
if (!create_pg_dir(local_data_directory, runtime_options.force))
|
||||
{
|
||||
log_error(_("unable to use directory %s ..."),
|
||||
log_error(_("unable to use directory %s"),
|
||||
local_data_directory);
|
||||
log_hint(_("use -F/--force to force this directory to be overwritten"));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
@@ -1008,10 +1049,15 @@ run_basebackup(void)
|
||||
|
||||
/*
|
||||
* As of 9.4, pg_basebackup only ever returns 0 or 1
|
||||
* XXX check for 10
|
||||
*/
|
||||
|
||||
r = system(script);
|
||||
|
||||
if (r !=0)
|
||||
return r;
|
||||
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1036,7 +1082,6 @@ run_file_backup(void)
|
||||
|
||||
char datadir_list_filename[MAXLEN];
|
||||
|
||||
struct BackupLabel backup_label;
|
||||
|
||||
if (mode == barman)
|
||||
{
|
||||
@@ -1518,17 +1563,68 @@ run_file_backup(void)
|
||||
fclose(tablespace_map_file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When using rsync, copy pg_control file last, emulating the base backup
|
||||
* protocol.
|
||||
*/
|
||||
if (mode == rsync)
|
||||
{
|
||||
char upstream_control_file[MAXPGPATH] = "";
|
||||
char local_control_file[MAXPGPATH] = "";
|
||||
|
||||
maxlen_snprintf(local_control_file, "%s/global", local_data_directory);
|
||||
|
||||
log_info(_("standby clone: local control file '%s'"),
|
||||
local_control_file);
|
||||
|
||||
if (!create_dir(local_control_file))
|
||||
{
|
||||
log_error(_("couldn't create directory %s"),
|
||||
local_control_file);
|
||||
goto stop_backup;
|
||||
}
|
||||
|
||||
maxlen_snprintf(upstream_control_file, "%s/global/pg_control",
|
||||
upstream_data_directory);
|
||||
log_debug("standby clone: upstream control file is \"%s\"",
|
||||
upstream_control_file);
|
||||
|
||||
r = copy_remote_files(runtime_options.host, runtime_options.remote_user,
|
||||
upstream_control_file, local_control_file,
|
||||
false, server_version_num);
|
||||
if (WEXITSTATUS(r))
|
||||
{
|
||||
log_warning(_("standby clone: failed copying upstreamcontrol file \"%s\""),
|
||||
upstream_control_file);
|
||||
r = ERR_BAD_SSH;
|
||||
goto stop_backup;
|
||||
}
|
||||
}
|
||||
|
||||
stop_backup:
|
||||
|
||||
if (mode == rsync && pg_start_backup_executed)
|
||||
{
|
||||
log_notice(_("notifying upstream about backup completion...\n"));
|
||||
log_notice(_("notifying upstream about backup completion"));
|
||||
if (stop_backup(source_conn, last_wal_segment, server_version_num) == false)
|
||||
{
|
||||
r = ERR_BAD_BASEBACKUP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* clean up copied data directory */
|
||||
if (mode == rsync)
|
||||
{
|
||||
cleanup_data_directory();
|
||||
}
|
||||
else if (mode == barman)
|
||||
{
|
||||
/* In Barman mode, remove local_repmgr_directory */
|
||||
rmtree(local_repmgr_tmp_directory, true);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1545,7 +1641,7 @@ make_barman_ssh_command(char *buf)
|
||||
|
||||
maxlen_snprintf(buf,
|
||||
"ssh %s barman%s",
|
||||
config_file_options.barman_server,
|
||||
config_file_options.barman_host,
|
||||
config_opt);
|
||||
|
||||
return buf;
|
||||
@@ -1684,6 +1780,67 @@ config_file_list_add(t_configfile_list *list, const char *file, const char *file
|
||||
list->entries ++;
|
||||
}
|
||||
|
||||
static void
|
||||
copy_configuration_files(void)
|
||||
{
|
||||
int i, r;
|
||||
t_configfile_info *file;
|
||||
char *host;
|
||||
|
||||
/* get host from upstream record */
|
||||
host = param_get(&recovery_conninfo, "host");
|
||||
|
||||
if (host == NULL)
|
||||
host = runtime_options.host;
|
||||
|
||||
log_verbose(LOG_DEBUG, "fetching configuration files from host \"%s\"", host);
|
||||
log_notice(_("copying external configuration files from upstream node"));
|
||||
|
||||
r = test_ssh_connection(host, runtime_options.remote_user);
|
||||
if (r != 0)
|
||||
{
|
||||
log_error(_("remote host %s is not reachable via SSH - unable to copy external configuration files"),
|
||||
host);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < config_files.entries; i++)
|
||||
{
|
||||
char dest_path[MAXPGPATH];
|
||||
file = config_files.files[i];
|
||||
|
||||
/*
|
||||
* Skip files in the data directory - these will be copied during
|
||||
* the main backup
|
||||
*/
|
||||
if (file->in_data_directory == true)
|
||||
continue;
|
||||
|
||||
if (runtime_options.copy_external_config_files_destination == CONFIG_FILE_SAMEPATH)
|
||||
{
|
||||
strncpy(dest_path, file->filepath, MAXPGPATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(dest_path, MAXPGPATH,
|
||||
"%s/%s",
|
||||
local_data_directory,
|
||||
file->filename);
|
||||
}
|
||||
|
||||
r = copy_remote_files(runtime_options.host, runtime_options.remote_user,
|
||||
file->filepath, dest_path, false, server_version_num);
|
||||
if (WEXITSTATUS(r))
|
||||
{
|
||||
log_error(_("standby clone: unable to copy config file \"%s\""),
|
||||
file->filename);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
get_tablespace_data(PGconn *upstream_conn, TablespaceDataList *list)
|
||||
@@ -1927,3 +2084,63 @@ read_backup_label(const char *local_data_directory, struct BackupLabel *out_back
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cleanup_data_directory(void)
|
||||
{
|
||||
char dirpath[MAXLEN] = "";
|
||||
|
||||
if (runtime_options.force)
|
||||
{
|
||||
/*
|
||||
* Remove any WAL files in the target directory which might have
|
||||
* been left over from previous use of this data directory;
|
||||
* rsync's --exclude option won't do this.
|
||||
*/
|
||||
|
||||
if (server_version_num >= 100000)
|
||||
maxlen_snprintf(dirpath, "%s/pg_wal/", local_data_directory);
|
||||
else
|
||||
maxlen_snprintf(dirpath, "%s/pg_xlog/", local_data_directory);
|
||||
|
||||
if (!rmtree(dirpath, false))
|
||||
{
|
||||
log_error(_("unable to empty local WAL directory %s"),
|
||||
dirpath);
|
||||
exit(ERR_BAD_RSYNC);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any existing replication slot directories from previous use
|
||||
* of this data directory; this matches the behaviour of a fresh
|
||||
* pg_basebackup, which would usually result in an empty pg_replslot
|
||||
* directory.
|
||||
*
|
||||
* If the backup label contains a nonzero
|
||||
* 'MIN FAILOVER SLOT LSN' entry we retain the slots and let
|
||||
* the server clean them up instead, matching pg_basebackup's
|
||||
* behaviour when failover slots are enabled.
|
||||
*
|
||||
* NOTE: watch out for any changes in the replication
|
||||
* slot directory name (as of 9.4: "pg_replslot") and
|
||||
* functionality of replication slots
|
||||
*/
|
||||
|
||||
if (server_version_num >= 90400 &&
|
||||
backup_label.min_failover_slot_lsn == InvalidXLogRecPtr)
|
||||
{
|
||||
maxlen_snprintf(dirpath, "%s/pg_replslot/",
|
||||
local_data_directory);
|
||||
|
||||
log_debug("deleting pg_replslot directory contents");
|
||||
|
||||
if (!rmtree(dirpath, false))
|
||||
{
|
||||
log_error(_("unable to empty replication slot directory \"%s\""),
|
||||
dirpath);
|
||||
exit(ERR_BAD_RSYNC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* values for --copy-external-config-files */
|
||||
#define CONFIG_FILE_SAMEPATH 1
|
||||
#define CONFIG_FILE_PGDATA 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* configuration metadata */
|
||||
@@ -44,12 +48,15 @@ typedef struct
|
||||
char data_dir[MAXPGPATH];
|
||||
|
||||
/* standby clone options */
|
||||
bool copy_external_config_files;
|
||||
int copy_external_config_files_destination;
|
||||
bool fast_checkpoint;
|
||||
bool rsync_only;
|
||||
bool no_upstream_connection;
|
||||
char recovery_min_apply_delay[MAXLEN];
|
||||
char replication_user[MAXLEN];
|
||||
char upstream_conninfo[MAXLEN];
|
||||
bool use_recovery_conninfo_password;
|
||||
char wal_keep_segments[MAXLEN];
|
||||
bool without_barman;
|
||||
|
||||
@@ -74,7 +81,7 @@ typedef struct
|
||||
/* node options */ \
|
||||
UNKNOWN_NODE_ID, "", "", \
|
||||
/* standby clone options */ \
|
||||
false, false, false, "", "", "", "", false, \
|
||||
false, CONFIG_FILE_SAMEPATH, false, false, false, "", "", "", false, "", false, \
|
||||
/* event options */ \
|
||||
false, "", 20 }
|
||||
|
||||
@@ -116,4 +123,6 @@ extern void print_error_list(ItemList *error_list, int log_level);
|
||||
|
||||
extern char * make_pg_path(char *file);
|
||||
|
||||
extern bool create_recovery_file(const char *data_dir, t_conninfo_param_list *recovery_conninfo);
|
||||
|
||||
#endif
|
||||
|
||||
272
repmgr-client.c
272
repmgr-client.c
@@ -16,9 +16,10 @@
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "repmgr.h"
|
||||
#include "compat.h"
|
||||
#include "repmgr-client.h"
|
||||
#include "repmgr-client-global.h"
|
||||
#include "repmgr-action-master.h"
|
||||
@@ -79,8 +80,69 @@ main(int argc, char **argv)
|
||||
*/
|
||||
logger_output_mode = OM_COMMAND_LINE;
|
||||
|
||||
/*
|
||||
* Initialize and pre-populate conninfo parameters; these will be
|
||||
* overwritten if matching command line parameters are provided.
|
||||
*
|
||||
* Only some actions will need these, but we need to do this before
|
||||
* the command line is parsed.
|
||||
*/
|
||||
|
||||
initialize_conninfo_params(&source_conninfo, true);
|
||||
|
||||
for (c = 0; c < source_conninfo.size && source_conninfo.keywords[c]; c++)
|
||||
{
|
||||
if (strcmp(source_conninfo.keywords[c], "host") == 0 &&
|
||||
(source_conninfo.values[c] != NULL))
|
||||
{
|
||||
strncpy(runtime_options.host, source_conninfo.values[c], MAXLEN);
|
||||
}
|
||||
else if (strcmp(source_conninfo.keywords[c], "hostaddr") == 0 &&
|
||||
(source_conninfo.values[c] != NULL))
|
||||
{
|
||||
strncpy(runtime_options.host, source_conninfo.values[c], MAXLEN);
|
||||
}
|
||||
else if (strcmp(source_conninfo.keywords[c], "port") == 0 &&
|
||||
(source_conninfo.values[c] != NULL))
|
||||
{
|
||||
strncpy(runtime_options.port, source_conninfo.values[c], MAXLEN);
|
||||
}
|
||||
else if (strcmp(source_conninfo.keywords[c], "dbname") == 0 &&
|
||||
(source_conninfo.values[c] != NULL))
|
||||
{
|
||||
strncpy(runtime_options.dbname, source_conninfo.values[c], MAXLEN);
|
||||
}
|
||||
else if (strcmp(source_conninfo.keywords[c], "user") == 0 &&
|
||||
(source_conninfo.values[c] != NULL))
|
||||
{
|
||||
strncpy(runtime_options.username, source_conninfo.values[c], MAXLEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Though libpq will default to the username as dbname, PQconndefaults()
|
||||
* doesn't return this
|
||||
*/
|
||||
if (runtime_options.dbname[0] == '\0')
|
||||
{
|
||||
strncpy(runtime_options.dbname, runtime_options.username, MAXLEN);
|
||||
}
|
||||
|
||||
|
||||
/* set default user for -R/--remote-user */
|
||||
{
|
||||
struct passwd *pw = NULL;
|
||||
|
||||
pw = getpwuid(geteuid());
|
||||
if (pw == NULL)
|
||||
{
|
||||
fprintf(stderr, _("could not get current user name: %s\n"), strerror(errno));
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
strncpy(runtime_options.username, pw->pw_name, MAXLEN);
|
||||
}
|
||||
|
||||
while ((c = getopt_long(argc, argv, "?Vb:f:Fd:h:p:U:R:S:L:vtD:cr", long_options,
|
||||
&optindex)) != -1)
|
||||
{
|
||||
@@ -209,6 +271,26 @@ main(int argc, char **argv)
|
||||
runtime_options.fast_checkpoint = true;
|
||||
break;
|
||||
|
||||
/* --copy-external-config-files(=[samepath|pgdata]) */
|
||||
case OPT_COPY_EXTERNAL_CONFIG_FILES:
|
||||
runtime_options.copy_external_config_files = true;
|
||||
if (optarg != NULL)
|
||||
{
|
||||
if (strcmp(optarg, "samepath") == 0)
|
||||
{
|
||||
runtime_options.copy_external_config_files_destination = CONFIG_FILE_SAMEPATH;
|
||||
}
|
||||
else if (strcmp(optarg, "pgdata") == 0)
|
||||
{
|
||||
runtime_options.copy_external_config_files_destination = CONFIG_FILE_PGDATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
item_list_append(&cli_errors, _("value provided for '--copy-external-config-files' must be 'samepath' or 'pgdata'"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* -r/--rsync-only */
|
||||
case 'r':
|
||||
runtime_options.rsync_only = true;
|
||||
@@ -254,6 +336,10 @@ main(int argc, char **argv)
|
||||
strncpy(runtime_options.upstream_conninfo, optarg, MAXLEN);
|
||||
break;
|
||||
|
||||
case OPT_USE_RECOVERY_CONNINFO_PASSWORD:
|
||||
runtime_options.use_recovery_conninfo_password = true;
|
||||
break;
|
||||
|
||||
case OPT_WITHOUT_BARMAN:
|
||||
runtime_options.without_barman = true;
|
||||
break;
|
||||
@@ -1705,3 +1791,187 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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-conne ) this might not be
|
||||
*/
|
||||
bool
|
||||
create_recovery_file(const char *data_dir, t_conninfo_param_list *recovery_conninfo)
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_debug("create_recovery_file(): creating \"%s\"...\n",
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
log_debug("recovery.conf: %s", line);
|
||||
|
||||
/* recovery_min_apply_delay = ... (optional) */
|
||||
if (*runtime_options.recovery_min_apply_delay)
|
||||
{
|
||||
maxlen_snprintf(line, "recovery_min_apply_delay = %s\n",
|
||||
runtime_options.recovery_min_apply_delay);
|
||||
if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false)
|
||||
return false;
|
||||
|
||||
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",
|
||||
repmgr_slot_name);
|
||||
if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false)
|
||||
return false;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
int c;
|
||||
char *escaped;
|
||||
|
||||
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 ||
|
||||
(runtime_options.use_recovery_conninfo_password == false &&
|
||||
strcmp(param_list->keywords[c], "password") == 0) ||
|
||||
(param_list->values[c] == NULL) ||
|
||||
(param_list->values[c] != NULL && param_list->values[c][0] == '\0'))
|
||||
continue;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
escaped = escape_recovery_conf_value(conninfo_buf.data);
|
||||
|
||||
maxlen_snprintf(line, "primary_conninfo = '%s'\n", escaped);
|
||||
|
||||
free(escaped);
|
||||
|
||||
termPQExpBuffer(&conninfo_buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,29 +33,31 @@
|
||||
#define BDR_UNREGISTER 19
|
||||
|
||||
/* command line options without short versions */
|
||||
#define OPT_HELP 1
|
||||
#define OPT_CHECK_UPSTREAM_CONFIG 2
|
||||
#define OPT_RECOVERY_MIN_APPLY_DELAY 3
|
||||
#define OPT_COPY_EXTERNAL_CONFIG_FILES 4
|
||||
#define OPT_CONFIG_ARCHIVE_DIR 5
|
||||
#define OPT_PG_REWIND 6
|
||||
#define OPT_PWPROMPT 7
|
||||
#define OPT_CSV 8
|
||||
#define OPT_NODE 9
|
||||
#define OPT_NODE_ID 10
|
||||
#define OPT_NODE_NAME 11
|
||||
#define OPT_WITHOUT_BARMAN 12
|
||||
#define OPT_NO_UPSTREAM_CONNECTION 13
|
||||
#define OPT_REGISTER_WAIT 14
|
||||
#define OPT_CLUSTER 15
|
||||
#define OPT_LOG_TO_FILE 16
|
||||
#define OPT_UPSTREAM_CONNINFO 17
|
||||
/* XXX deprecate, replace with --use-conninfo-password (--use-recovery-conninfo-password) set */
|
||||
#define OPT_NO_CONNINFO_PASSWORD 18
|
||||
#define OPT_REPLICATION_USER 19
|
||||
#define OPT_EVENT 20
|
||||
#define OPT_LIMIT 21
|
||||
#define OPT_ALL 22
|
||||
#define OPT_HELP 1
|
||||
#define OPT_CHECK_UPSTREAM_CONFIG 2
|
||||
#define OPT_RECOVERY_MIN_APPLY_DELAY 3
|
||||
#define OPT_COPY_EXTERNAL_CONFIG_FILES 4
|
||||
#define OPT_CONFIG_ARCHIVE_DIR 5
|
||||
#define OPT_PG_REWIND 6
|
||||
#define OPT_PWPROMPT 7
|
||||
#define OPT_CSV 8
|
||||
#define OPT_NODE 9
|
||||
#define OPT_NODE_ID 10
|
||||
#define OPT_NODE_NAME 11
|
||||
#define OPT_WITHOUT_BARMAN 12
|
||||
#define OPT_NO_UPSTREAM_CONNECTION 13
|
||||
#define OPT_REGISTER_WAIT 14
|
||||
#define OPT_CLUSTER 15
|
||||
#define OPT_LOG_TO_FILE 16
|
||||
#define OPT_UPSTREAM_CONNINFO 17
|
||||
/* replaces --no-conninfo-password */
|
||||
#define OPT_USE_RECOVERY_CONNINFO_PASSWORD 18
|
||||
#define OPT_REPLICATION_USER 19
|
||||
#define OPT_EVENT 20
|
||||
#define OPT_LIMIT 21
|
||||
#define OPT_ALL 22
|
||||
/* deprecated since 3.3 */
|
||||
#define OPT_NO_CONNINFO_PASSWORD 999
|
||||
|
||||
|
||||
static struct option long_options[] =
|
||||
@@ -91,12 +93,14 @@ static struct option long_options[] =
|
||||
{"verbose", no_argument, NULL, 'v'},
|
||||
|
||||
/* standby clone options */
|
||||
{"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},
|
||||
{"fast-checkpoint", no_argument, NULL, 'c'},
|
||||
{"rsync-only", no_argument, NULL, 'r'},
|
||||
{"no-upstream-connection", no_argument, NULL, OPT_NO_UPSTREAM_CONNECTION},
|
||||
{"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY},
|
||||
{"replication-user", required_argument, NULL, OPT_REPLICATION_USER},
|
||||
{"upstream-conninfo", required_argument, NULL, OPT_UPSTREAM_CONNINFO},
|
||||
{"use-recovery-conninfo-password", no_argument, NULL, OPT_USE_RECOVERY_CONNINFO_PASSWORD},
|
||||
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
|
||||
|
||||
/* event options */
|
||||
@@ -104,6 +108,9 @@ static struct option long_options[] =
|
||||
{"event", required_argument, NULL, OPT_EVENT },
|
||||
{"limit", required_argument, NULL, OPT_LIMIT },
|
||||
|
||||
/* deprecated */
|
||||
{"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD},
|
||||
|
||||
/* not yet handled */
|
||||
{"wal-keep-segments", required_argument, NULL, 'w'},
|
||||
{"keep-history", required_argument, NULL, 'k'},
|
||||
@@ -120,10 +127,10 @@ static struct option long_options[] =
|
||||
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
|
||||
{"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},
|
||||
{"wait-sync", optional_argument, NULL, OPT_REGISTER_WAIT},
|
||||
{"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD},
|
||||
/* Following options for internal use */
|
||||
{"cluster", required_argument, NULL, OPT_CLUSTER},
|
||||
{"config-archive-dir", required_argument, NULL, OPT_CONFIG_ARCHIVE_DIR},
|
||||
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
@@ -138,4 +145,7 @@ static void exit_with_errors(void);
|
||||
static void print_item_list(ItemList *item_list);
|
||||
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
|
||||
|
||||
14
strutil.c
14
strutil.c
@@ -26,7 +26,7 @@ xvsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
|
||||
if (retval >= (int) size)
|
||||
{
|
||||
log_error(_("buffer of size not large enough to format entire string '%s'"),
|
||||
log_error(_("buffer of specified size not large enough to format entire string '%s'"),
|
||||
str);
|
||||
exit(ERR_STR_OVERFLOW);
|
||||
}
|
||||
@@ -61,6 +61,18 @@ maxlen_snprintf(char *str, const char *format,...)
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
maxpath_snprintf(char *str, const char *format,...)
|
||||
{
|
||||
va_list arglist;
|
||||
int retval;
|
||||
|
||||
va_start(arglist, format);
|
||||
retval = xvsnprintf(str, MAXPGPATH, format, arglist);
|
||||
va_end(arglist);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
append_where_clause(PQExpBufferData *where_clause, const char *format, ...)
|
||||
|
||||
@@ -40,6 +40,10 @@ extern int
|
||||
maxlen_snprintf(char *str, const char *format,...)
|
||||
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
|
||||
|
||||
extern int
|
||||
maxpath_snprintf(char *str, const char *format,...)
|
||||
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
|
||||
|
||||
extern void
|
||||
item_list_append(ItemList *item_list, const char *message);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user