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"));
|
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);
|
canonicalize_path(config_file_path);
|
||||||
|
|
||||||
if (stat(config_file_path, &stat_config) == 0)
|
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"));
|
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)
|
if (stat(config_file_path, &stat_config) == 0)
|
||||||
{
|
{
|
||||||
config_file_found = true;
|
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);
|
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)
|
if (stat(config_file_path, &stat_config) == 0)
|
||||||
{
|
{
|
||||||
config_file_found = true;
|
config_file_found = true;
|
||||||
@@ -486,10 +486,9 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList *
|
|||||||
*/
|
*/
|
||||||
if (known_parameter == true && !strlen(value)) {
|
if (known_parameter == true && !strlen(value)) {
|
||||||
char error_message_buf[MAXLEN] = "";
|
char error_message_buf[MAXLEN] = "";
|
||||||
snprintf(error_message_buf,
|
maxlen_snprintf(error_message_buf,
|
||||||
MAXLEN,
|
_("\"%s\": no value provided"),
|
||||||
_("\"%s\": no value provided"),
|
name);
|
||||||
name);
|
|
||||||
|
|
||||||
item_list_append(error_list, error_message_buf);
|
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;
|
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
|
int
|
||||||
get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record)
|
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);
|
log_verbose(LOG_DEBUG, "get_slot_record():\n%s", query.data);
|
||||||
|
|
||||||
res = PQexec(conn, query.data);
|
res = PQexec(conn, query.data);
|
||||||
|
|
||||||
|
termPQExpBuffer(&query);
|
||||||
|
|
||||||
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
{
|
{
|
||||||
log_error(_("unable to query pg_replication_slots:\n %s"),
|
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 */
|
/* replication slot functions */
|
||||||
bool create_replication_slot(PGconn *conn, char *slot_name, int server_version_num, PQExpBufferData *error_msg);
|
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);
|
int get_slot_record(PGconn *conn, char *slot_name, t_replication_slot *record);
|
||||||
|
|
||||||
/* backup functions */
|
/* backup functions */
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ static char *last_wal_segment = NULL;
|
|||||||
|
|
||||||
static bool pg_start_backup_executed = false;
|
static bool pg_start_backup_executed = false;
|
||||||
|
|
||||||
|
static struct BackupLabel backup_label;
|
||||||
|
|
||||||
/* used by barman mode */
|
/* used by barman mode */
|
||||||
static char local_repmgr_tmp_directory[MAXPGPATH];
|
static char local_repmgr_tmp_directory[MAXPGPATH];
|
||||||
static char barman_command_buf[MAXLEN] = "";
|
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 initialise_direct_clone(void);
|
||||||
static void config_file_list_init(t_configfile_list *list, int max_size);
|
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 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_basebackup(void);
|
||||||
static int run_file_backup(void);
|
static int run_file_backup(void);
|
||||||
@@ -306,27 +310,64 @@ do_standby_clone(void)
|
|||||||
if (mode == pg_basebackup)
|
if (mode == pg_basebackup)
|
||||||
{
|
{
|
||||||
r = run_basebackup();
|
r = run_basebackup();
|
||||||
if (r != 0)
|
|
||||||
{
|
|
||||||
log_warning(_("standby clone: base backup failed"));
|
|
||||||
|
|
||||||
r = ERR_BAD_BASEBACKUP;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
r = run_file_backup();
|
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
|
void
|
||||||
check_barman_config(void)
|
check_barman_config(void)
|
||||||
{
|
{
|
||||||
@@ -678,7 +719,7 @@ initialise_direct_clone(void)
|
|||||||
|
|
||||||
if (!create_pg_dir(local_data_directory, runtime_options.force))
|
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);
|
local_data_directory);
|
||||||
log_hint(_("use -F/--force to force this directory to be overwritten"));
|
log_hint(_("use -F/--force to force this directory to be overwritten"));
|
||||||
exit(ERR_BAD_CONFIG);
|
exit(ERR_BAD_CONFIG);
|
||||||
@@ -1008,10 +1049,15 @@ run_basebackup(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* As of 9.4, pg_basebackup only ever returns 0 or 1
|
* As of 9.4, pg_basebackup only ever returns 0 or 1
|
||||||
|
* XXX check for 10
|
||||||
*/
|
*/
|
||||||
|
|
||||||
r = system(script);
|
r = system(script);
|
||||||
|
|
||||||
|
if (r !=0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,7 +1082,6 @@ run_file_backup(void)
|
|||||||
|
|
||||||
char datadir_list_filename[MAXLEN];
|
char datadir_list_filename[MAXLEN];
|
||||||
|
|
||||||
struct BackupLabel backup_label;
|
|
||||||
|
|
||||||
if (mode == barman)
|
if (mode == barman)
|
||||||
{
|
{
|
||||||
@@ -1518,17 +1563,68 @@ run_file_backup(void)
|
|||||||
fclose(tablespace_map_file);
|
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:
|
stop_backup:
|
||||||
|
|
||||||
if (mode == rsync && pg_start_backup_executed)
|
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)
|
if (stop_backup(source_conn, last_wal_segment, server_version_num) == false)
|
||||||
{
|
{
|
||||||
r = ERR_BAD_BASEBACKUP;
|
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;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1545,7 +1641,7 @@ make_barman_ssh_command(char *buf)
|
|||||||
|
|
||||||
maxlen_snprintf(buf,
|
maxlen_snprintf(buf,
|
||||||
"ssh %s barman%s",
|
"ssh %s barman%s",
|
||||||
config_file_options.barman_server,
|
config_file_options.barman_host,
|
||||||
config_opt);
|
config_opt);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
@@ -1684,6 +1780,67 @@ config_file_list_add(t_configfile_list *list, const char *file, const char *file
|
|||||||
list->entries ++;
|
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
|
static int
|
||||||
get_tablespace_data(PGconn *upstream_conn, TablespaceDataList *list)
|
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;
|
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"
|
#include "config.h"
|
||||||
|
|
||||||
|
/* values for --copy-external-config-files */
|
||||||
|
#define CONFIG_FILE_SAMEPATH 1
|
||||||
|
#define CONFIG_FILE_PGDATA 2
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/* configuration metadata */
|
/* configuration metadata */
|
||||||
@@ -44,12 +48,15 @@ typedef struct
|
|||||||
char data_dir[MAXPGPATH];
|
char data_dir[MAXPGPATH];
|
||||||
|
|
||||||
/* standby clone options */
|
/* standby clone options */
|
||||||
|
bool copy_external_config_files;
|
||||||
|
int copy_external_config_files_destination;
|
||||||
bool fast_checkpoint;
|
bool fast_checkpoint;
|
||||||
bool rsync_only;
|
bool rsync_only;
|
||||||
bool no_upstream_connection;
|
bool no_upstream_connection;
|
||||||
char recovery_min_apply_delay[MAXLEN];
|
char recovery_min_apply_delay[MAXLEN];
|
||||||
char replication_user[MAXLEN];
|
char replication_user[MAXLEN];
|
||||||
char upstream_conninfo[MAXLEN];
|
char upstream_conninfo[MAXLEN];
|
||||||
|
bool use_recovery_conninfo_password;
|
||||||
char wal_keep_segments[MAXLEN];
|
char wal_keep_segments[MAXLEN];
|
||||||
bool without_barman;
|
bool without_barman;
|
||||||
|
|
||||||
@@ -74,7 +81,7 @@ typedef struct
|
|||||||
/* node options */ \
|
/* node options */ \
|
||||||
UNKNOWN_NODE_ID, "", "", \
|
UNKNOWN_NODE_ID, "", "", \
|
||||||
/* standby clone options */ \
|
/* standby clone options */ \
|
||||||
false, false, false, "", "", "", "", false, \
|
false, CONFIG_FILE_SAMEPATH, false, false, false, "", "", "", false, "", false, \
|
||||||
/* event options */ \
|
/* event options */ \
|
||||||
false, "", 20 }
|
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 char * make_pg_path(char *file);
|
||||||
|
|
||||||
|
extern bool create_recovery_file(const char *data_dir, t_conninfo_param_list *recovery_conninfo);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
272
repmgr-client.c
272
repmgr-client.c
@@ -16,9 +16,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "repmgr.h"
|
#include "repmgr.h"
|
||||||
|
#include "compat.h"
|
||||||
#include "repmgr-client.h"
|
#include "repmgr-client.h"
|
||||||
#include "repmgr-client-global.h"
|
#include "repmgr-client-global.h"
|
||||||
#include "repmgr-action-master.h"
|
#include "repmgr-action-master.h"
|
||||||
@@ -79,8 +80,69 @@ main(int argc, char **argv)
|
|||||||
*/
|
*/
|
||||||
logger_output_mode = OM_COMMAND_LINE;
|
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);
|
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,
|
while ((c = getopt_long(argc, argv, "?Vb:f:Fd:h:p:U:R:S:L:vtD:cr", long_options,
|
||||||
&optindex)) != -1)
|
&optindex)) != -1)
|
||||||
{
|
{
|
||||||
@@ -209,6 +271,26 @@ main(int argc, char **argv)
|
|||||||
runtime_options.fast_checkpoint = true;
|
runtime_options.fast_checkpoint = true;
|
||||||
break;
|
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 */
|
/* -r/--rsync-only */
|
||||||
case 'r':
|
case 'r':
|
||||||
runtime_options.rsync_only = true;
|
runtime_options.rsync_only = true;
|
||||||
@@ -254,6 +336,10 @@ main(int argc, char **argv)
|
|||||||
strncpy(runtime_options.upstream_conninfo, optarg, MAXLEN);
|
strncpy(runtime_options.upstream_conninfo, optarg, MAXLEN);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPT_USE_RECOVERY_CONNINFO_PASSWORD:
|
||||||
|
runtime_options.use_recovery_conninfo_password = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case OPT_WITHOUT_BARMAN:
|
case OPT_WITHOUT_BARMAN:
|
||||||
runtime_options.without_barman = true;
|
runtime_options.without_barman = true;
|
||||||
break;
|
break;
|
||||||
@@ -1705,3 +1791,187 @@ copy_remote_files(char *host, char *remote_user, char *remote_path,
|
|||||||
return r;
|
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
|
#define BDR_UNREGISTER 19
|
||||||
|
|
||||||
/* command line options without short versions */
|
/* command line options without short versions */
|
||||||
#define OPT_HELP 1
|
#define OPT_HELP 1
|
||||||
#define OPT_CHECK_UPSTREAM_CONFIG 2
|
#define OPT_CHECK_UPSTREAM_CONFIG 2
|
||||||
#define OPT_RECOVERY_MIN_APPLY_DELAY 3
|
#define OPT_RECOVERY_MIN_APPLY_DELAY 3
|
||||||
#define OPT_COPY_EXTERNAL_CONFIG_FILES 4
|
#define OPT_COPY_EXTERNAL_CONFIG_FILES 4
|
||||||
#define OPT_CONFIG_ARCHIVE_DIR 5
|
#define OPT_CONFIG_ARCHIVE_DIR 5
|
||||||
#define OPT_PG_REWIND 6
|
#define OPT_PG_REWIND 6
|
||||||
#define OPT_PWPROMPT 7
|
#define OPT_PWPROMPT 7
|
||||||
#define OPT_CSV 8
|
#define OPT_CSV 8
|
||||||
#define OPT_NODE 9
|
#define OPT_NODE 9
|
||||||
#define OPT_NODE_ID 10
|
#define OPT_NODE_ID 10
|
||||||
#define OPT_NODE_NAME 11
|
#define OPT_NODE_NAME 11
|
||||||
#define OPT_WITHOUT_BARMAN 12
|
#define OPT_WITHOUT_BARMAN 12
|
||||||
#define OPT_NO_UPSTREAM_CONNECTION 13
|
#define OPT_NO_UPSTREAM_CONNECTION 13
|
||||||
#define OPT_REGISTER_WAIT 14
|
#define OPT_REGISTER_WAIT 14
|
||||||
#define OPT_CLUSTER 15
|
#define OPT_CLUSTER 15
|
||||||
#define OPT_LOG_TO_FILE 16
|
#define OPT_LOG_TO_FILE 16
|
||||||
#define OPT_UPSTREAM_CONNINFO 17
|
#define OPT_UPSTREAM_CONNINFO 17
|
||||||
/* XXX deprecate, replace with --use-conninfo-password (--use-recovery-conninfo-password) set */
|
/* replaces --no-conninfo-password */
|
||||||
#define OPT_NO_CONNINFO_PASSWORD 18
|
#define OPT_USE_RECOVERY_CONNINFO_PASSWORD 18
|
||||||
#define OPT_REPLICATION_USER 19
|
#define OPT_REPLICATION_USER 19
|
||||||
#define OPT_EVENT 20
|
#define OPT_EVENT 20
|
||||||
#define OPT_LIMIT 21
|
#define OPT_LIMIT 21
|
||||||
#define OPT_ALL 22
|
#define OPT_ALL 22
|
||||||
|
/* deprecated since 3.3 */
|
||||||
|
#define OPT_NO_CONNINFO_PASSWORD 999
|
||||||
|
|
||||||
|
|
||||||
static struct option long_options[] =
|
static struct option long_options[] =
|
||||||
@@ -91,12 +93,14 @@ static struct option long_options[] =
|
|||||||
{"verbose", no_argument, NULL, 'v'},
|
{"verbose", no_argument, NULL, 'v'},
|
||||||
|
|
||||||
/* standby clone options */
|
/* standby clone options */
|
||||||
|
{"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},
|
||||||
{"fast-checkpoint", no_argument, NULL, 'c'},
|
{"fast-checkpoint", no_argument, NULL, 'c'},
|
||||||
{"rsync-only", no_argument, NULL, 'r'},
|
{"rsync-only", no_argument, NULL, 'r'},
|
||||||
{"no-upstream-connection", no_argument, NULL, OPT_NO_UPSTREAM_CONNECTION},
|
{"no-upstream-connection", no_argument, NULL, OPT_NO_UPSTREAM_CONNECTION},
|
||||||
{"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY},
|
{"recovery-min-apply-delay", required_argument, NULL, OPT_RECOVERY_MIN_APPLY_DELAY},
|
||||||
{"replication-user", required_argument, NULL, OPT_REPLICATION_USER},
|
{"replication-user", required_argument, NULL, OPT_REPLICATION_USER},
|
||||||
{"upstream-conninfo", required_argument, NULL, OPT_UPSTREAM_CONNINFO},
|
{"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},
|
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
|
||||||
|
|
||||||
/* event options */
|
/* event options */
|
||||||
@@ -104,6 +108,9 @@ static struct option long_options[] =
|
|||||||
{"event", required_argument, NULL, OPT_EVENT },
|
{"event", required_argument, NULL, OPT_EVENT },
|
||||||
{"limit", required_argument, NULL, OPT_LIMIT },
|
{"limit", required_argument, NULL, OPT_LIMIT },
|
||||||
|
|
||||||
|
/* deprecated */
|
||||||
|
{"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD},
|
||||||
|
|
||||||
/* not yet handled */
|
/* not yet handled */
|
||||||
{"wal-keep-segments", required_argument, NULL, 'w'},
|
{"wal-keep-segments", required_argument, NULL, 'w'},
|
||||||
{"keep-history", required_argument, NULL, 'k'},
|
{"keep-history", required_argument, NULL, 'k'},
|
||||||
@@ -120,10 +127,10 @@ static struct option long_options[] =
|
|||||||
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
|
{"without-barman", no_argument, NULL, OPT_WITHOUT_BARMAN},
|
||||||
{"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},
|
{"copy-external-config-files", optional_argument, NULL, OPT_COPY_EXTERNAL_CONFIG_FILES},
|
||||||
{"wait-sync", optional_argument, NULL, OPT_REGISTER_WAIT},
|
{"wait-sync", optional_argument, NULL, OPT_REGISTER_WAIT},
|
||||||
{"no-conninfo-password", no_argument, NULL, OPT_NO_CONNINFO_PASSWORD},
|
|
||||||
/* Following options for internal use */
|
/* Following options for internal use */
|
||||||
{"cluster", required_argument, NULL, OPT_CLUSTER},
|
{"cluster", required_argument, NULL, OPT_CLUSTER},
|
||||||
{"config-archive-dir", required_argument, NULL, OPT_CONFIG_ARCHIVE_DIR},
|
{"config-archive-dir", required_argument, NULL, OPT_CONFIG_ARCHIVE_DIR},
|
||||||
|
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,4 +145,7 @@ static void exit_with_errors(void);
|
|||||||
static void print_item_list(ItemList *item_list);
|
static void print_item_list(ItemList *item_list);
|
||||||
static void check_cli_parameters(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
|
#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)
|
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);
|
str);
|
||||||
exit(ERR_STR_OVERFLOW);
|
exit(ERR_STR_OVERFLOW);
|
||||||
}
|
}
|
||||||
@@ -61,6 +61,18 @@ maxlen_snprintf(char *str, const char *format,...)
|
|||||||
return retval;
|
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
|
void
|
||||||
append_where_clause(PQExpBufferData *where_clause, const char *format, ...)
|
append_where_clause(PQExpBufferData *where_clause, const char *format, ...)
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ extern int
|
|||||||
maxlen_snprintf(char *str, const char *format,...)
|
maxlen_snprintf(char *str, const char *format,...)
|
||||||
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
|
__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
|
extern void
|
||||||
item_list_append(ItemList *item_list, const char *message);
|
item_list_append(ItemList *item_list, const char *message);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user