*
* pg_probackup.c: Backup/Recovery manager for PostgreSQL.
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 2009-2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Portions Copyright (c) 2015-2019, Postgres Professional
*
*-------------------------------------------------------------------------
*/
#include "pg_probackup.h"
#include "getopt_long.h"
#include "streamutil.h"
#include "file.h"
#include <sys/stat.h>
#include <sys/vfs.h>
#include "tool_common.h"
#include "configuration.h"
#include "thread.h"
#include <time.h>
#include "common/fe_memutils.h"
#include "storage/file/fio_device.h"
#include "storage/dss/dss_adaptor.h"
#include <sys/resource.h>
#include "oss/include/restore.h"
#include "common_cipher.h"
#include "catalog/catalog.h"
#define MIN_ULIMIT_STACK_SIZE 8388608
#define PROG_NAME "gs_probackup"
const char *PROGRAM_NAME = NULL;
* if any */
const char *PROGRAM_NAME_FULL = NULL;
const char *PROGRAM_FULL_PATH = NULL;
typedef enum ProbackupSubcmd
{
NO_CMD = 0,
INIT_CMD,
ADD_INSTANCE_CMD,
DELETE_INSTANCE_CMD,
BACKUP_CMD,
RESTORE_CMD,
VALIDATE_CMD,
DELETE_CMD,
MERGE_CMD,
SHOW_CMD,
SET_CONFIG_CMD,
SET_BACKUP_CMD,
SHOW_CONFIG_CMD,
} ProbackupSubcmd;
char *backup_path = NULL;
* path or to the data files in the backup catalog
* $BACKUP_PATH/backups/instance_name
*/
char backup_instance_path[MAXPGPATH];
* path or to the wal files in the backup catalog
* $BACKUP_PATH/wal/instance_name
*/
char arclog_path[MAXPGPATH] = "";
char *externaldir = NULL;
static char *backup_id_string = NULL;
int num_threads = 1;
bool stream_wal = true;
pid_t my_pid = 0;
__thread int my_thread_num = 1;
bool progress = false;
bool no_sync = false;
#if PG_VERSION_NUM >= 100000
char *replication_slot = NULL;
#endif
bool temp_slot = false;
char *password = NULL;
int rw_timeout = 0;
bool backup_logs = false;
bool backup_replslots = false;
bool smooth_checkpoint;
char *remote_agent;
static char *backup_note = NULL;
static char *oss_status_string = NULL;
static char *target_time = NULL;
static char *target_xid = NULL;
static char *target_lsn = NULL;
static char *target_inclusive = NULL;
static TimeLineID target_tli;
static char *target_stop;
static bool target_immediate;
static char *target_name = NULL;
static char *target_action = NULL;
static pgRecoveryTarget *recovery_target_options = NULL;
static pgRestoreParams *restore_params = NULL;
time_t current_time = 0;
bool no_validate = false;
IncrRestoreMode incremental_mode = INCR_NONE;
bool skip_block_validation = false;
bool skip_external_dirs = false;
bool specify_extdir = false;
bool specify_tbsdir = false;
bool delete_wal = false;
bool delete_expired = false;
bool merge_expired = false;
bool force = false;
bool dry_run = false;
static char *delete_status = NULL;
bool compress_shortcut = false;
char *instance_name;
int batch_size = 1;
static char *wal_file_path;
static char *wal_file_name;
static bool file_overwrite = false;
static bool no_ready_rename = false;
static char *prefetch_dir;
bool no_validate_wal = false;
ShowFormat show_format = SHOW_PLAIN;
bool show_archive = false;
int64 ttl = -1;
static char *expire_time_string = NULL;
static pgSetBackupParams *set_backup_params = NULL;
pgBackup current;
static ProbackupSubcmd backup_subcmd = NO_CMD;
bool gen_key = false;
int key_type = KEY_TYPE_INVALID;
char* encrypt_mode = NULL;
char* encrypt_key = NULL;
char* encrypt_salt = NULL;
char* encrypt_dev_params = NULL;
void* crypto_module_session = NULL;
void* crypto_module_keyctx = NULL;
void* crypto_hmac_keyctx = NULL;
bool enc_flag = false;
void* oss_client = NULL;
char* TS_DIR_WITH_PGXC = NULL;
char* NODE_NAME_WITH_PGXC = NULL;
static void set_ts_ver_dir_real_remote(char* nodename);
static bool help_opt = false;
static void opt_incr_restore_mode(ConfigOption *opt, const char *arg);
static void opt_backup_mode(ConfigOption *opt, const char *arg);
static void opt_show_format(ConfigOption *opt, const char *arg);
static void opt_media_type(ConfigOption *opt, const char *arg);
static void compress_init(void);
static void dss_init(void);
static void check_unlimit_stack_size(void);
* Short name should be non-printable ASCII character.
* Use values between 128 and 255.
*/
static ConfigOption cmd_options[] =
{
{ 'b', 130, "help", &help_opt, SOURCE_CMD_STRICT },
{ 's', 'B', "backup-path", &backup_path, SOURCE_CMD_STRICT },
{ 'u', 'j', "threads", &num_threads, SOURCE_CMD_STRICT },
{ 'b', 131, "stream", &stream_wal, SOURCE_CMD_STRICT },
{ 'b', 132, "progress", &progress, SOURCE_CMD_STRICT },
{ 's', 'i', "backup-id", &backup_id_string, SOURCE_CMD_STRICT },
{ 'b', 133, "no-sync", &no_sync, SOURCE_CMD_STRICT },
{ 'b', 180, "backup-pg-log", &backup_logs, SOURCE_CMD_STRICT },
{ 'f', 'b', "backup-mode", (void *)opt_backup_mode, SOURCE_CMD_STRICT },
{ 'b', 'C', "smooth-checkpoint", &smooth_checkpoint, SOURCE_CMD_STRICT },
{ 's', 'S', "slot", &replication_slot, SOURCE_CMD_STRICT },
{ 'b', 181, "temp-slot", &temp_slot, SOURCE_CMD_STRICT },
{ 'b', 182, "delete-wal", &delete_wal, SOURCE_CMD_STRICT },
{ 'b', 183, "delete-expired", &delete_expired, SOURCE_CMD_STRICT },
{ 'b', 184, "merge-expired", &merge_expired, SOURCE_CMD_STRICT },
{ 'b', 185, "dry-run", &dry_run, SOURCE_CMD_STRICT },
{ 's', 238, "note", &backup_note, SOURCE_CMD_STRICT },
{ 's', 136, "recovery-target-time", &target_time, SOURCE_CMD_STRICT },
{ 's', 137, "recovery-target-xid", &target_xid, SOURCE_CMD_STRICT },
{ 's', 144, "recovery-target-lsn", &target_lsn, SOURCE_CMD_STRICT },
{ 's', 138, "recovery-target-inclusive", &target_inclusive, SOURCE_CMD_STRICT },
{ 'f', 'T', "tablespace-mapping", (void *)opt_tablespace_map, SOURCE_CMD_STRICT },
{ 'f', 155, "external-mapping", (void *)opt_externaldir_map, SOURCE_CMD_STRICT },
{ 's', 141, "recovery-target-name", &target_name, SOURCE_CMD_STRICT },
{ 'b', 143, "no-validate", &no_validate, SOURCE_CMD_STRICT },
{ 'b', 154, "skip-block-validation", &skip_block_validation, SOURCE_CMD_STRICT },
{ 'b', 156, "skip-external-dirs", &skip_external_dirs, SOURCE_CMD_STRICT },
{ 'f', 'I', "incremental-mode", (void *)opt_incr_restore_mode, SOURCE_CMD_STRICT },
{ 's', 187, "with-encryption", &encrypt_mode, SOURCE_CMD_STRICT},
{ 's', 188, "with-key", &encrypt_key, SOURCE_CMD_STRICT},
{ 's', 189, "with-salt", &encrypt_salt, SOURCE_CMD_STRICT},
{ 's', 190, "with-device-params", &encrypt_dev_params, SOURCE_CMD_STRICT},
{ 'b', 191, "gen-key", &gen_key, SOURCE_CMD_STRICT},
{ 'b', 145, "wal", &delete_wal, SOURCE_CMD_STRICT },
{ 'b', 146, "expired", &delete_expired, SOURCE_CMD_STRICT },
{ 's', 172, "status", &delete_status, SOURCE_CMD_STRICT },
{ 'b', 186, "backup-pg-replslot", &backup_replslots, SOURCE_CMD_STRICT},
{ 'b', 147, "force", &force, SOURCE_CMD_STRICT },
{ 'b', 148, "compress", &compress_shortcut, SOURCE_CMD_STRICT },
{ 'B', 'w', "no-password", &prompt_password, SOURCE_CMD_STRICT },
{ 's', 'W', "password", &password, SOURCE_CMD_STRICT },
{ 'u', 't', "rw-timeout", &rw_timeout, SOURCE_CMD_STRICT },
{ 's', 149, "instance", &instance_name, SOURCE_CMD_STRICT },
{ 's', 150, "wal-file-path", &wal_file_path, SOURCE_CMD_STRICT },
{ 's', 151, "wal-file-name", &wal_file_name, SOURCE_CMD_STRICT },
{ 'b', 152, "overwrite", &file_overwrite, SOURCE_CMD_STRICT },
{ 'b', 153, "no-ready-rename", &no_ready_rename, SOURCE_CMD_STRICT },
{ 'i', 162, "batch-size", &batch_size, SOURCE_CMD_STRICT },
{ 's', 163, "prefetch-dir", &prefetch_dir, SOURCE_CMD_STRICT },
{ 'b', 164, "no-validate-wal", &no_validate_wal, SOURCE_CMD_STRICT },
{ 'f', 165, "format", (void *)opt_show_format, SOURCE_CMD_STRICT },
{ 'b', 166, "archive", &show_archive, SOURCE_CMD_STRICT },
{ 'I', 170, "ttl", &ttl, SOURCE_CMD_STRICT, SOURCE_DEFAULT, 0, OPTION_UNIT_S, option_get_value},
{ 's', 171, "expire-time", &expire_time_string, SOURCE_CMD_STRICT },
{ 's', 136, "time", &target_time, SOURCE_CMD_STRICT },
{ 's', 137, "xid", &target_xid, SOURCE_CMD_STRICT },
{ 's', 138, "inclusive", &target_inclusive, SOURCE_CMD_STRICT },
{ 'u', 139, "timeline", &target_tli, SOURCE_CMD_STRICT },
{ 's', 144, "lsn", &target_lsn, SOURCE_CMD_STRICT },
{ 'b', 140, "immediate", &target_immediate, SOURCE_CMD_STRICT },
{ 'f', 'M', "media-type", (void *)opt_media_type, SOURCE_CMD_STRICT },
{ 's', 241, "s3-status", &oss_status_string, SOURCE_CMD_STRICT },
{ 0 }
};
static void
setMyLocation(void)
{
#ifdef WIN32
if (IsSshProtocol())
elog(ERROR, "Currently remote operations on Windows are not supported");
#endif
MyLocation = IsSshProtocol()
? (backup_subcmd == BACKUP_CMD || backup_subcmd == RESTORE_CMD || backup_subcmd == ADD_INSTANCE_CMD)
? FIO_BACKUP_HOST
: FIO_LOCAL_HOST
: FIO_LOCAL_HOST;
}
static void parse_non_subcommand_option(char *option, int argc, char *argv[])
{
#ifdef WIN32
if (strcmp(argv[1], "ssh") == 0)
launch_ssh(argv);
#endif
if (strcmp(argv[1], "agent") == 0)
{
if (argc == 3) {
set_ts_ver_dir_real_remote(argv[2]);
}
if (argc > 3) {
elog(ERROR,
"Version mismatch, gs_probackup binary with version '%s' "
"is launched as an agent for gs_probackup binary with version '%s'",
PROGRAM_VERSION, argv[3]);
}
fio_communicate(STDIN_FILENO, STDOUT_FILENO);
exit(0);
}
else if (strcmp(argv[1], "--help") == 0 ||
strcmp(argv[1], "-?") == 0 ||
strcmp(argv[1], "help") == 0)
{
if (argc > 2)
help_command(argv[2]);
else
help_pg_probackup();
}
else if (strcmp(argv[1], "--version") == 0
|| strcmp(argv[1], "version") == 0
|| strcmp(argv[1], "-V") == 0)
{
puts("gs_probackup " DEF_GS_VERSION);
exit(0);
}
else
elog(ERROR, "Unknown subcommand \"%s\"", argv[1]);
}
static void parse_options(char *option, int argc, char *argv[])
{
if (strcmp(option, "add-instance") == 0)
backup_subcmd = ADD_INSTANCE_CMD;
else if (strcmp(option, "del-instance") == 0)
backup_subcmd = DELETE_INSTANCE_CMD;
else if (strcmp(option, "init") == 0)
backup_subcmd = INIT_CMD;
else if (strcmp(option, "backup") == 0)
backup_subcmd = BACKUP_CMD;
else if (strcmp(option, "restore") == 0)
backup_subcmd = RESTORE_CMD;
else if (strcmp(option, "validate") == 0)
backup_subcmd = VALIDATE_CMD;
else if (strcmp(option, "delete") == 0)
backup_subcmd = DELETE_CMD;
else if (strcmp(option, "merge") == 0)
backup_subcmd = MERGE_CMD;
else if (strcmp(option, "show") == 0)
backup_subcmd = SHOW_CMD;
else if (strcmp(option, "set-config") == 0)
backup_subcmd = SET_CONFIG_CMD;
else if (strcmp(option, "set-backup") == 0)
backup_subcmd = SET_BACKUP_CMD;
else if (strcmp(option, "show-config") == 0)
backup_subcmd = SHOW_CONFIG_CMD;
else
parse_non_subcommand_option(option, argc, argv);
}
static char *make_command_string(int argc, char *argv[])
{
char *command = NULL;
errno_t rc = 0;
if (backup_subcmd == BACKUP_CMD ||
backup_subcmd == RESTORE_CMD ||
backup_subcmd == VALIDATE_CMD ||
backup_subcmd == DELETE_CMD ||
backup_subcmd == MERGE_CMD ||
backup_subcmd == SET_CONFIG_CMD ||
backup_subcmd == SET_BACKUP_CMD)
{
int i,
len = 0,
allocated = 0;
allocated = sizeof(char) * MAXPGPATH;
command = (char *) gs_palloc0(allocated);
for (i = 0; i < argc; i++)
{
int arglen = strlen(argv[i]);
if (arglen + len > allocated)
{
allocated *= 2;
command = (char *)gs_repalloc(command, allocated);
}
rc = strncpy_s(command + len, allocated - len, argv[i], arglen);
securec_check_c(rc, "", "");
len += arglen;
command[len++] = ' ';
}
command[len] = '\0';
}
return command;
}
static void parse_instance_name()
{
errno_t rc = 0;
* Option --instance is required for all commands except
* init, show and validate
*/
if (instance_name == NULL)
{
if (backup_subcmd != INIT_CMD && backup_subcmd != SHOW_CMD &&
backup_subcmd != VALIDATE_CMD)
elog(ERROR, "required parameter not specified: --instance");
}
else
instance_config.name = pgut_strdup(instance_name);
* If --instance option was passed, construct paths for backup data and
* xlog files of this backup instance.
*/
if ((backup_path != NULL) && instance_name)
{
* Fill global variables used to generate pathes inside the instance's
* backup catalog.
* TODO replace global variables with InstanceConfig structure fields
*/
rc = sprintf_s(backup_instance_path, MAXPGPATH, "%s/%s/%s",
backup_path, BACKUPS_DIR, instance_name);
securec_check_ss_c(rc, "", "");
rc = sprintf_s(arclog_path, MAXPGPATH, "%s/%s/%s", backup_path, "wal", instance_name);
securec_check_ss_c(rc, "", "");
* Fill InstanceConfig structure fields used to generate pathes inside
* the instance's backup catalog.
* TODO continue refactoring to use these fields instead of global vars
*/
rc = sprintf_s(instance_config.backup_instance_path, MAXPGPATH, "%s/%s/%s",
backup_path, BACKUPS_DIR, instance_name);
securec_check_ss_c(rc, "", "");
canonicalize_path(instance_config.backup_instance_path);
rc = sprintf_s(instance_config.arclog_path, MAXPGPATH, "%s/%s/%s",
backup_path, "wal", instance_name);
securec_check_ss_c(rc, "", "");
canonicalize_path(instance_config.arclog_path);
* Ensure that requested backup instance exists.
* for all commands except init, which doesn't take this parameter,
* add-instance, which creates new instance
*/
if (backup_subcmd != INIT_CMD && backup_subcmd != ADD_INSTANCE_CMD)
{
struct stat st;
if (current.media_type == MEDIA_TYPE_OSS) {
restoreConfigDir();
}
if (fio_stat(backup_instance_path, &st, true, FIO_BACKUP_HOST) != 0)
{
elog(WARNING, "Failed to access directory \"%s\": %s",
backup_instance_path, strerror(errno));
elog(ERROR, "Instance '%s' does not exist in this backup catalog",
instance_name);
}
else
{
if (!S_ISDIR(st.st_mode))
elog(ERROR, "-B, --backup-path must be a path to directory");
}
}
}
* We read options from command line, now we need to read them from
* configuration file since we got backup path and instance name.
* For some commands an instance option isn't required, see above.
*/
if (instance_name)
{
char path[MAXPGPATH];
config_get_opt_env(instance_options);
if (backup_subcmd != ADD_INSTANCE_CMD)
{
join_path_components(path, backup_instance_path,
BACKUP_CATALOG_CONF_FILE);
if (current.media_type == MEDIA_TYPE_OSS) {
restoreConfigFile(path);
}
config_read_opt(path, instance_options, ERROR, true, false);
if (current.media_type == MEDIA_TYPE_OSS) {
uploadConfigFile(path, path);
}
}
setMyLocation();
}
}
static void parse_cmdline_args(int argc, char *argv[], const char *command_name)
{
config_get_opt(argc, argv, cmd_options, instance_options);
if (password) {
if (!prompt_password) {
elog(ERROR, "You cannot specify --password and --no-password options together");
}
replace_password(argc, argv, "-W");
replace_password(argc, argv, "--password");
}
if (specify_tbsdir && !specify_extdir) {
elog(ERROR, "If specify --tablespace-mapping option, you must specify --external-mapping option together");
}
pgut_init();
if (help_opt)
help_command(command_name);
if (backup_path == NULL)
{
* If command line argument is not set, try to read BACKUP_PATH
* from environment variable
*/
backup_path = getenv("BACKUP_PATH");
if (backup_path == NULL)
elog(ERROR, "required parameter not specified: BACKUP_PATH (-B, --backup-path)");
}
setMyLocation();
if (backup_path != NULL)
{
canonicalize_path(backup_path);
if (!is_absolute_path(backup_path))
elog(ERROR, "-B, --backup-path must be an absolute path");
}
if (backup_path && !is_absolute_path(backup_path))
elog(ERROR, "-B, --backup-path must be an absolute path");
parse_instance_name();
}
static void do_delete_operate()
{
if (delete_expired && backup_id_string)
elog(ERROR, "You cannot specify --delete-expired and (-i, --backup-id) options together");
if (merge_expired && backup_id_string)
elog(ERROR, "You cannot specify --merge-expired and (-i, --backup-id) options together");
if (delete_status && backup_id_string)
elog(ERROR, "You cannot specify --status and (-i, --backup-id) options together");
if (!delete_expired && !merge_expired && !delete_wal && delete_status == NULL && !backup_id_string)
elog(ERROR, "You must specify at least one of the delete options: "
"--delete-expired |--delete-wal |--merge-expired |--status |(-i, --backup-id)");
if (!backup_id_string)
{
if (delete_status)
do_delete_status(&instance_config, delete_status);
else
do_retention();
}
else
do_delete(current.backup_id);
}
static int do_validate_operate()
{
if (current.backup_id == 0 && target_time == 0 && target_xid == 0 && !target_lsn &&
!recovery_target_options->target_name) {
return do_validate_all();
} else {
return do_restore_or_validate(current.backup_id,
recovery_target_options,
restore_params,
no_sync);
}
}
static int do_actual_operate()
{
int res = 0;
pgut_atexit_push(unlink_lock_atexit, NULL);
switch (backup_subcmd)
{
case ADD_INSTANCE_CMD:
return do_add_instance(&instance_config);
case DELETE_INSTANCE_CMD:
res = do_delete_instance();
break;
case INIT_CMD:
return do_init();
case BACKUP_CMD:
{
time_t start_time = time(NULL);
current.stream = stream_wal;
if (current.backup_mode == BACKUP_MODE_INVALID)
elog(ERROR, "required parameter not specified: BACKUP_MODE "
"(-b, --backup-mode)");
res = do_backup(start_time, set_backup_params, no_validate, no_sync, backup_logs, backup_replslots);
break;
}
case RESTORE_CMD:
res = do_restore_or_validate(current.backup_id,
recovery_target_options,
restore_params, no_sync);
break;
case VALIDATE_CMD:
res = do_validate_operate();
break;
case SHOW_CMD:
return do_show(instance_name, current.backup_id, show_archive);
case DELETE_CMD:
do_delete_operate();
break;
case MERGE_CMD:
do_merge(current.backup_id);
break;
case SHOW_CONFIG_CMD:
do_show_config();
break;
case SET_CONFIG_CMD:
do_set_config(false);
break;
case SET_BACKUP_CMD:
if (!backup_id_string)
elog(ERROR, "You must specify parameter (-i, --backup-id) for 'set-backup' command");
do_set_backup(instance_name, current.backup_id, set_backup_params);
break;
case NO_CMD:
elog(ERROR, "Unknown subcommand");
}
delete_backup_directory(instance_name);
on_cleanup();
release_logfile();
clearCrypto(crypto_module_session, crypto_module_keyctx, crypto_hmac_keyctx);
return res;
}
static void parse_backup_option_to_params(char *command, char *command_name)
{
if (backup_subcmd == SET_BACKUP_CMD || backup_subcmd == BACKUP_CMD)
{
time_t expire_time = 0;
oss_status_t oss_status = OSS_STATUS_INVALID;
if (expire_time_string && ttl >= 0)
elog(ERROR, "You cannot specify '--expire-time' and '--ttl' options together");
if (expire_time_string)
{
if (!parse_time(expire_time_string, &expire_time, false))
elog(ERROR, "Invalid value for '--expire-time' option: '%s'",
expire_time_string);
}
if (oss_status_string) {
oss_status = str2ossStatus(oss_status_string);
}
if (expire_time > 0 || ttl >= 0 || backup_note || oss_status_string)
{
set_backup_params = pgut_new(pgSetBackupParams);
set_backup_params->ttl = ttl;
set_backup_params->expire_time = expire_time;
set_backup_params->note = backup_note;
set_backup_params->oss_status = oss_status;
if (backup_note && strlen(backup_note) > MAX_NOTE_SIZE)
elog(ERROR, "Backup note cannot exceed %u bytes", MAX_NOTE_SIZE);
}
}
if (backup_subcmd == VALIDATE_CMD && restore_params->no_validate)
elog(ERROR, "You cannot specify \"--no-validate\" option with the \"%s\" command",
command_name);
if (num_threads < 1)
num_threads = 1;
if (batch_size < 1)
batch_size = 1;
}
static void check_restore_option(char *command_name)
{
if (backup_subcmd == VALIDATE_CMD || backup_subcmd == RESTORE_CMD)
{
* Parse all recovery target options into recovery_target_options
* structure.
*/
recovery_target_options =
parseRecoveryTargetOptions(target_time, target_xid,
target_inclusive, target_tli, target_lsn,
(target_stop != NULL) ? target_stop :
(target_immediate) ? "immediate" : NULL,
target_name, target_action);
if (force && backup_subcmd != RESTORE_CMD)
elog(ERROR, "You cannot specify \"--force\" flag with the \"%s\" command",
command_name);
if (force)
no_validate = true;
restore_params = pgut_new(pgRestoreParams);
restore_params->is_restore = (backup_subcmd == RESTORE_CMD);
restore_params->force = force;
restore_params->no_validate = no_validate;
restore_params->skip_block_validation = skip_block_validation;
restore_params->skip_external_dirs = skip_external_dirs;
restore_params->incremental_mode = incremental_mode;
}
}
static void check_unlimit_stack_size(void)
{
bool DssFlags = IsDssMode();
if (DssFlags == false || (DssFlags == true && backup_subcmd != RESTORE_CMD))
return;
struct rlimit lim;
if (getrlimit(RLIMIT_STACK, &lim) != 0) {
elog(ERROR, "getrlimit RLIMIT_STACK failed.");
}
if ((int)lim.rlim_cur < MIN_ULIMIT_STACK_SIZE) {
elog(ERROR, "current ulimit stack size is %d,"
" please run the ulimit -s size(size >= 8192) command to change it.", lim.rlim_cur);
}
}
static void check_backid_option(char *command_name)
{
if (backup_id_string != NULL)
{
if (backup_subcmd != RESTORE_CMD &&
backup_subcmd != VALIDATE_CMD &&
backup_subcmd != DELETE_CMD &&
backup_subcmd != MERGE_CMD &&
backup_subcmd != SET_BACKUP_CMD &&
backup_subcmd != SHOW_CMD)
elog(ERROR, "Cannot use -i (--backup-id) option together with the \"%s\" command",
command_name);
current.backup_id = base36dec(backup_id_string);
if (current.backup_id == 0)
elog(ERROR, "Invalid backup-id \"%s\"", backup_id_string);
}
if (!instance_config.conn_opt.pghost &&
instance_config.remote.host &&
IsSshProtocol()) {
instance_config.conn_opt.pghost = instance_config.remote.host;
}
if (instance_config.conn_opt.pghost != NULL)
dbhost = gs_pstrdup(instance_config.conn_opt.pghost);
if (instance_config.conn_opt.pgport != NULL)
dbport = gs_pstrdup(instance_config.conn_opt.pgport);
if (instance_config.conn_opt.pguser != NULL)
dbuser = gs_pstrdup(instance_config.conn_opt.pguser);
}
static void check_dss_input()
{
if (!instance_config.dss.enable_dss && instance_config.dss.vgname != NULL) {
instance_config.dss.enable_dss = true;
}
if (instance_config.dss.enable_dss && instance_config.dss.socketpath == NULL) {
instance_config.dss.socketpath = getSocketpathFromEnv();
}
if (instance_config.dss.socketpath != NULL) {
char* trueSocketpath = getSocketpathFromEnv();
if (strcmp(instance_config.dss.socketpath, trueSocketpath) != 0) {
elog(ERROR, "The input socket:%s is incorrect", instance_config.dss.socketpath);
}
}
}
static void check_threads_num_option()
{
#ifdef _SC_NPROCESSORS_ONLN
int nprocs = sysconf(_SC_NPROCESSORS_ONLN);
if (nprocs <= 0) {
elog(ERROR, "Failed to get the number of available cores.");
}
if (num_threads > nprocs * 10) {
elog(ERROR, "Invalid value for '-j' option: %d, the number of cores available is %d, "
"more than 10 times is not allowed.", num_threads, nprocs);
}
#endif
}
void sync_file_with_permissions(char* filepath, mode_t original_permissions)
{
(void)chmod(filepath, S_IRUSR | S_IWUSR);
if (fio_sync(filepath, FIO_BACKUP_HOST) != 0) {
(void)chmod(filepath, original_permissions);
elog(ERROR, "Cannot sync file \"%s\": %s", filepath, strerror(errno));
}
(void)chmod(filepath, original_permissions);
}
int main(int argc, char *argv[])
{
char *command = NULL,
*command_name;
PROGRAM_NAME_FULL = argv[0];
pgBackupInit(¤t);
init_config(&instance_config, instance_name);
PROGRAM_NAME = get_progname(argv[0]);
PROGRAM_FULL_PATH = (char *)gs_palloc0(MAXPGPATH);
current_time = time(NULL);
my_pid = getpid();
#if PG_VERSION_NUM >= 110000
* Reset WAL segment size, we will retreive it using RetrieveWalSegSize()
* later.
*/
WalSegSz = 0;
#endif
* Save main thread's tid. It is used call exit() in case of errors.
*/
main_tid = pthread_self();
init_audit(PROG_NAME, argc, argv);
if (argc > 1)
{
parse_options(argv[1], argc, argv);
}
if (backup_subcmd == NO_CMD)
elog(ERROR, "No subcommand specified");
* Make command string before getopt_long() will call. It permutes the
* content of argv.
*/
command_name = gs_pstrdup(argv[1]);
command = make_command_string(argc, argv);
optind += 1;
parse_cmdline_args(argc, argv, command_name);
init_logger(backup_path, &instance_config.logger);
if (command)
{
elog_file(INFO, "command: %s", command);
pfree(command);
command = NULL;
}
if (find_my_exec(argv[0],(char *) const_cast<char*>(PROGRAM_FULL_PATH)) < 0)
{
pfree((char *) const_cast<char*>(PROGRAM_FULL_PATH));
PROGRAM_FULL_PATH = NULL;
elog(WARNING, "%s: could not find a full path to executable", PROGRAM_NAME);
}
* We have read pgdata path from command line or from configuration file.
* Ensure that pgdata is an absolute path.
*/
if (instance_config.pgdata != NULL)
canonicalize_path(instance_config.pgdata);
if (instance_config.pgdata != NULL &&
!is_absolute_path(instance_config.pgdata))
elog(ERROR, "-D, --pgdata must be an absolute path");
if (instance_config.pgdata != NULL)
{
errno_t rc = strcpy_s(g_datadir.pg_data, strlen(instance_config.pgdata) + 1, instance_config.pgdata);
securec_check_c(rc, "\0", "\0");
}
#if PG_VERSION_NUM >= 110000
if (instance_name &&
backup_subcmd != INIT_CMD &&
backup_subcmd != ADD_INSTANCE_CMD && backup_subcmd != SET_CONFIG_CMD &&
!IsValidWalSegSize(instance_config.xlog_seg_size))
{
* then xlog_seg_size is equal to zero. Manually set it to 16MB.
*/
if (instance_config.xlog_seg_size == 0)
instance_config.xlog_seg_size = DEFAULT_XLOG_SEG_SIZE;
else
elog(ERROR, "Invalid WAL segment size %u", instance_config.xlog_seg_size);
}
#endif
check_backid_option(command_name);
check_restore_option(command_name);
check_threads_num_option();
* Parse set-backup options into set_backup_params structure.
*/
parse_backup_option_to_params(command, command_name);
pfree(command_name);
compress_init();
check_dss_input();
dss_init();
check_unlimit_stack_size();
initDataPathStruct(IsDssMode());
errno_t rc = do_actual_operate();
if (rc == 0) {
audit_success();
}
return rc;
}
static void
opt_incr_restore_mode(ConfigOption *opt, const char *arg)
{
if (pg_strcasecmp(arg, "none") == 0)
{
incremental_mode = INCR_NONE;
return;
}
else if (pg_strcasecmp(arg, "checksum") == 0)
{
incremental_mode = INCR_CHECKSUM;
return;
}
else if (pg_strcasecmp(arg, "lsn") == 0)
{
incremental_mode = INCR_LSN;
return;
}
elog(ERROR, "Invalid value for '--incremental-mode' option: '%s'", arg);
}
static void
opt_backup_mode(ConfigOption *opt, const char *arg)
{
current.backup_mode = parse_backup_mode(arg);
}
static void
opt_media_type(ConfigOption *opt, const char *arg)
{
current.media_type = parse_media_type(arg);
}
static void
opt_show_format(ConfigOption *opt, const char *arg)
{
const char *v = arg;
size_t len;
while (IsSpace(*v))
v++;
len = strlen(v);
if (len > 0)
{
if (pg_strncasecmp("plain", v, len) == 0)
show_format = SHOW_PLAIN;
else if (pg_strncasecmp("json", v, len) == 0)
show_format = SHOW_JSON;
else
elog(ERROR, "Invalid show format \"%s\"", arg);
}
else
elog(ERROR, "Invalid show format \"%s\"", arg);
}
* Initialize compress and sanity checks for compress.
*/
static void
compress_init(void)
{
if (compress_shortcut)
instance_config.compress_alg = ZLIB_COMPRESS;
if (backup_subcmd != SET_CONFIG_CMD)
{
if (instance_config.compress_level != COMPRESS_LEVEL_DEFAULT
&& instance_config.compress_alg == NOT_DEFINED_COMPRESS)
elog(ERROR, "Cannot specify compress-level option alone without "
"compress-algorithm option");
}
if (instance_config.compress_level < 0 || instance_config.compress_level > 9)
elog(ERROR, "--compress-level value must be in the range from 0 to 9");
if (instance_config.compress_alg == ZLIB_COMPRESS && instance_config.compress_level == 0)
elog(WARNING, "Compression level 0 will lead to data bloat!");
if (instance_config.compress_alg == LZ4_COMPRESS && instance_config.compress_level > 1)
{
elog(WARNING, "Compression level will be set to 1 due to lz4 only supports level 0 and 1!");
}
if (backup_subcmd == BACKUP_CMD)
{
#ifndef HAVE_LIBZ
if (instance_config.compress_alg == ZLIB_COMPRESS)
elog(ERROR, "This build does not support zlib compression");
#endif
}
}
static void dss_init(void)
{
if (IsDssMode()) {
if (backup_subcmd == DELETE_CMD || backup_subcmd == DELETE_INSTANCE_CMD ||
backup_subcmd == SHOW_CMD || backup_subcmd == MERGE_CMD) {
return;
}
if (dss_device_init(instance_config.dss.socketpath, IsDssMode()) != DSS_SUCCESS) {
elog(ERROR, "fail to init dss device");
return;
}
if (IsSshProtocol()) {
elog(ERROR, "Remote operations on dss mode are not supported");
}
if (instance_config.dss.vgname == NULL) {
elog(ERROR, "Vgname must be specified in dss mode.");
}
parse_vgname_args(instance_config.dss.vgname);
struct stat st;
if (stat(instance_config.dss.vgdata, &st) != 0 || !S_ISDIR(st.st_mode)) {
elog(ERROR, "Could not connect dssserver, vgdata: \"%s\", socketpath: \"%s\", check and retry later.",
instance_config.dss.vgdata, instance_config.dss.socketpath);
}
if (strlen(instance_config.dss.vglog) && (stat(instance_config.dss.vglog, &st) != 0 || !S_ISDIR(st.st_mode))) {
elog(ERROR, "Could not connect dssserver, vglog: \"%s\", socketpath: \"%s\", check and retry later.",
instance_config.dss.vglog, instance_config.dss.socketpath);
}
if (backup_subcmd != RESTORE_CMD) {
off_t size = 0;
char xlog_control_path[MAXPGPATH];
join_path_components(xlog_control_path, instance_config.dss.vgdata, PG_XLOG_CONTROL_FILE);
size = dss_get_file_size(xlog_control_path);
if (size != (off_t)(PG_CONTROL_SIZE * (REFORMER_CTL_INSTANCEID + 1))) {
elog(ERROR, "Invalid size of file \"%s\", file_size: %ld\n", xlog_control_path, size);
}
}
errno_t rc = strcpy_s(g_datadir.dss_data, strlen(instance_config.dss.vgdata) + 1, instance_config.dss.vgdata);
securec_check_c(rc, "\0", "\0");
rc = strcpy_s(g_datadir.dss_log, strlen(instance_config.dss.vglog) + 1, instance_config.dss.vglog);
securec_check_c(rc, "\0", "\0");
XLogSegmentSize = DSS_XLOG_SEG_SIZE;
instance_config.xlog_seg_size = DSS_XLOG_SEG_SIZE;
}
}
static void set_ts_ver_dir_real_remote(char* nodename)
{
errno_t rc = 0;
NODE_NAME_WITH_PGXC = (char*)pg_malloc(strlen(nodename) + 1);
pgut_atexit_push(pg_free_callback, NODE_NAME_WITH_PGXC);
rc = strncpy_s(NODE_NAME_WITH_PGXC, strlen(nodename) + 1, nodename, strlen(nodename));
securec_check_c(rc, "", "");
TS_DIR_WITH_PGXC = (char*)pg_malloc(sizeof(TABLESPACE_VERSION_DIRECTORY "_") + strlen(nodename) + 1);
pgut_atexit_push(pg_free_callback, TS_DIR_WITH_PGXC);
rc = strncpy_s(TS_DIR_WITH_PGXC, sizeof(TABLESPACE_VERSION_DIRECTORY "_"), TABLESPACE_VERSION_DIRECTORY "_",
sizeof(TABLESPACE_VERSION_DIRECTORY "_") - 1);
securec_check_c(rc, "", "");
if (IsDssMode()) {
return;
}
rc = strncpy_s(TS_DIR_WITH_PGXC + sizeof(TABLESPACE_VERSION_DIRECTORY "_") - 1, strlen(nodename) + 1, nodename,
strlen(nodename));
securec_check_c(rc, "", "");
}