diff --git a/config.c b/config.c index 204964a2..0a5699ed 100644 --- a/config.c +++ b/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); } diff --git a/dbutils.c b/dbutils.c index 02566ec7..31be15f9 100644 --- a/dbutils.c +++ b/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"), diff --git a/dbutils.h b/dbutils.h index ba226ce7..b303ee63 100644 --- a/dbutils.h +++ b/dbutils.h @@ -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 */ diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index 8cf3815c..d4d516f5 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -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); + } + } +} diff --git a/repmgr-client-global.h b/repmgr-client-global.h index 18bea725..4a21bec8 100644 --- a/repmgr-client-global.h +++ b/repmgr-client-global.h @@ -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 diff --git a/repmgr-client.c b/repmgr-client.c index 69d0281b..1fe39f85 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -16,9 +16,10 @@ */ #include - +#include #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); +} + + diff --git a/repmgr-client.h b/repmgr-client.h index a1fbe78c..4d82b52d 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -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 diff --git a/strutil.c b/strutil.c index 583731cb..56ee0599 100644 --- a/strutil.c +++ b/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, ...) diff --git a/strutil.h b/strutil.h index cfa2ea2b..d71989db 100644 --- a/strutil.h +++ b/strutil.h @@ -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);