1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2024-11-24 08:52:38 +02:00

1. Implement backup of multiple instances. Add option --instance, commands add-instance and del-instance.

2. Implement pg_probackup specific commands for archiving: archive-push for archive_command and archive-get for restore_command
This commit is contained in:
Anastasia 2017-05-29 18:52:43 +03:00
parent 5390887107
commit 4e94454544
16 changed files with 527 additions and 81 deletions

View File

@ -19,6 +19,7 @@ OBJS = backup.o \
xlogreader.o \
streamutil.o \
receivelog.o \
archive.o \
utils/parray.o \
utils/pgut.o \
utils/logger.o

88
archive.c Normal file
View File

@ -0,0 +1,88 @@
/*-------------------------------------------------------------------------
*
* archive.c: - pg_probackup specific archive commands for archive backups.
*
*
* Portions Copyright (c) 2017, Postgres Professional
*
*-------------------------------------------------------------------------
*/
#include "pg_probackup.h"
#include <unistd.h>
#include <sys/stat.h>
/*
* pg_probackup specific archive command for archive backups
* set archive_command = 'pg_probackup archive-push -B /home/anastasia/backup
* --wal-file-path %p --wal-file-name %f', to move backups into arclog_path.
* Where archlog_path is $BACKUP_PATH/wal/system_id.
* Currently it just copies wal files to the new location.
* TODO Planned options: compress, list the arclog content,
* compute and validate checksums.
*/
int
do_archive_push(char *wal_file_path, char *wal_file_name)
{
char backup_wal_file_path[MAXPGPATH];
char absolute_wal_file_path[MAXPGPATH];
char current_dir[MAXPGPATH];
int64 system_id;
pgBackupConfig *config;
if (!getcwd(current_dir, sizeof(current_dir)))
elog(ERROR, "getcwd() error");
/* verify that archive-push --instance parameter is valid */
config = readBackupCatalogConfigFile();
system_id = get_system_identifier(current_dir);
if (config->pgdata == NULL)
elog(ERROR, "cannot read pg_probackup.conf for this instance");
if(system_id != config->system_identifier)
elog(ERROR, "Refuse to push WAL segment %s into archive. Instance parameters mismatch."
"Instance '%s' should have SYSTEM_ID = %ld instead of %ld",
wal_file_name, instance_name, config->system_identifier, system_id);
if (strcmp(current_dir, config->pgdata) != 0)
elog(ERROR, "Refuse to push WAL segment %s into archive. Instance parameters mismatch."
"Instance '%s' should have PGDATA = %s instead of %s",
wal_file_name, instance_name, config->pgdata, current_dir);
/* Create 'archlog_path' directory. Do nothing if it already exists. */
dir_create_dir(arclog_path, DIR_PERMISSION);
join_path_components(absolute_wal_file_path, current_dir, wal_file_path);
join_path_components(backup_wal_file_path, arclog_path, wal_file_name);
elog(INFO, "pg_probackup archive-push from %s to %s", absolute_wal_file_path, backup_wal_file_path);
copy_wal_file(absolute_wal_file_path, backup_wal_file_path);
elog(INFO, "pg_probackup archive-push completed successfully");
return 0;
}
/*
* pg_probackup specific restore command.
* Move files from arclog_path to pgdata/wal_file_path.
*/
int
do_archive_get(char *wal_file_path, char *wal_file_name)
{
char backup_wal_file_path[MAXPGPATH];
char absolute_wal_file_path[MAXPGPATH];
char current_dir[MAXPGPATH];
if (!getcwd(current_dir, sizeof(current_dir)))
elog(ERROR, "getcwd() error");
join_path_components(absolute_wal_file_path, current_dir, wal_file_path);
join_path_components(backup_wal_file_path, arclog_path, wal_file_name);
elog(INFO, "pg_probackup archive-get from %s to %s", backup_wal_file_path, absolute_wal_file_path);
copy_wal_file(backup_wal_file_path, absolute_wal_file_path);
elog(INFO, "pg_probackup archive-get completed successfully");
return 0;
}

View File

@ -512,7 +512,7 @@ check_system_identifiers(void)
uint64 system_id_pgdata;
char *val;
system_id_pgdata = get_system_identifier();
system_id_pgdata = get_system_identifier(pgdata);
res = pgut_execute(backup_conn,
"SELECT system_identifier FROM pg_control_system()",

View File

@ -50,7 +50,7 @@ catalog_lock(void)
pid_t my_pid,
my_p_pid;
join_path_components(lock_file, backup_path, BACKUP_CATALOG_PID);
join_path_components(lock_file, backup_instance_path, BACKUP_CATALOG_PID);
/*
* If the PID in the lockfile is our own PID or our parent's or
@ -84,7 +84,7 @@ catalog_lock(void)
/*
* We need a loop here because of race conditions. But don't loop forever
* (for example, a non-writable $backup_path directory might cause a failure
* (for example, a non-writable $backup_instance_path directory might cause a failure
* that won't go away). 100 tries seems like plenty.
*/
for (ntries = 0;; ntries++)
@ -253,16 +253,14 @@ catalog_get_backup_list(time_t requested_backup_id)
{
DIR *date_dir = NULL;
struct dirent *date_ent = NULL;
char backups_path[MAXPGPATH];
parray *backups = NULL;
pgBackup *backup = NULL;
/* open backup root directory */
join_path_components(backups_path, backup_path, BACKUPS_DIR);
date_dir = opendir(backups_path);
/* open backup instance backups directory */
date_dir = opendir(backup_instance_path);
if (date_dir == NULL)
{
elog(WARNING, "cannot open directory \"%s\": %s", backups_path,
elog(WARNING, "cannot open directory \"%s\": %s", backup_instance_path,
strerror(errno));
goto err_proc;
}
@ -275,12 +273,12 @@ catalog_get_backup_list(time_t requested_backup_id)
char date_path[MAXPGPATH];
/* skip not-directory entries and hidden entries */
if (!IsDir(backups_path, date_ent->d_name)
if (!IsDir(backup_instance_path, date_ent->d_name)
|| date_ent->d_name[0] == '.')
continue;
/* open subdirectory of specific backup */
join_path_components(date_path, backups_path, date_ent->d_name);
join_path_components(date_path, backup_instance_path, date_ent->d_name);
/* read backup information from BACKUP_CONTROL_FILE */
snprintf(backup_conf_path, MAXPGPATH, "%s/%s", date_path, BACKUP_CONTROL_FILE);
@ -309,7 +307,7 @@ catalog_get_backup_list(time_t requested_backup_id)
if (errno)
{
elog(WARNING, "cannot read backup root directory \"%s\": %s",
backups_path, strerror(errno));
backup_instance_path, strerror(errno));
goto err_proc;
}
@ -620,9 +618,9 @@ pgBackupGetPath(const pgBackup *backup, char *path, size_t len, const char *subd
datetime = base36enc(backup->start_time);
if (subdir)
snprintf(path, len, "%s/%s/%s/%s", backup_path, BACKUPS_DIR, datetime, subdir);
snprintf(path, len, "%s/%s/%s", backup_instance_path, datetime, subdir);
else
snprintf(path, len, "%s/%s/%s", backup_path, BACKUPS_DIR, datetime);
snprintf(path, len, "%s/%s", backup_instance_path, datetime);
free(datetime);
make_native_path(path);

View File

@ -121,8 +121,7 @@ writeBackupCatalogConfigFile(pgBackupConfig *config)
char path[MAXPGPATH];
FILE *fp;
join_path_components(path, backup_path, BACKUPS_DIR);
join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE);
join_path_components(path, backup_instance_path, BACKUP_CATALOG_CONF_FILE);
fp = fopen(path, "wt");
if (fp == NULL)
elog(ERROR, "cannot create %s: %s",
@ -164,8 +163,7 @@ readBackupCatalogConfigFile(void)
cur_config = config;
join_path_components(path, backup_path, BACKUPS_DIR);
join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE);
join_path_components(path, backup_instance_path, BACKUP_CATALOG_CONF_FILE);
pgBackupConfigInit(config);
pgut_readopt(path, options, ERROR);

99
data.c
View File

@ -679,6 +679,105 @@ copy_file(const char *from_root, const char *to_root, pgFile *file)
return true;
}
/* Almost like copy file, except the fact we don't calculate checksum */
void
copy_wal_file(const char *from_path, const char *to_path)
{
FILE *in;
FILE *out;
size_t read_len = 0;
int errno_tmp;
char buf[XLOG_BLCKSZ];
struct stat st;
/* open file for read */
in = fopen(from_path, "r");
if (in == NULL)
{
/* maybe deleted, it's not error */
if (errno == ENOENT)
elog(ERROR, "cannot open source WAL file \"%s\": %s", from_path,
strerror(errno));
}
/* open backup file for write */
out = fopen(to_path, "w");
if (out == NULL)
{
int errno_tmp = errno;
fclose(in);
elog(ERROR, "cannot open destination file \"%s\": %s",
to_path, strerror(errno_tmp));
}
/* stat source file to change mode of destination file */
if (fstat(fileno(in), &st) == -1)
{
fclose(in);
fclose(out);
elog(ERROR, "cannot stat \"%s\": %s", from_path,
strerror(errno));
}
if (st.st_size > XLOG_SEG_SIZE)
elog(ERROR, "Unexpected wal file size %s : %ld", from_path, st.st_size);
/* copy content */
for (;;)
{
if ((read_len = fread(buf, 1, sizeof(buf), in)) != sizeof(buf))
break;
if (fwrite(buf, 1, read_len, out) != read_len)
{
errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR, "cannot write to \"%s\": %s", to_path,
strerror(errno_tmp));
}
}
errno_tmp = errno;
if (!feof(in))
{
fclose(in);
fclose(out);
elog(ERROR, "cannot read backup mode file \"%s\": %s",
from_path, strerror(errno_tmp));
}
/* copy odd part */
if (read_len > 0)
{
if (fwrite(buf, 1, read_len, out) != read_len)
{
errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR, "cannot write to \"%s\": %s", to_path,
strerror(errno_tmp));
}
}
/* update file permission. */
if (chmod(to_path, st.st_mode) == -1)
{
errno_tmp = errno;
fclose(in);
fclose(out);
elog(ERROR, "cannot change mode of \"%s\": %s", to_path,
strerror(errno_tmp));
}
fclose(in);
fclose(out);
}
/*
* Save part of the file into backup.
* skip_size - size of the file in previous backup. We can skip it

View File

@ -384,3 +384,48 @@ delete_walfiles(XLogRecPtr oldest_lsn, TimeLineID oldest_tli)
elog(WARNING, "could not open archive location \"%s\": %s",
arclog_path, strerror(errno));
}
/* Delete all backup files and wal files of given instance. */
int
do_delete_instance(void)
{
parray *backup_list;
int i;
char instance_config_path[MAXPGPATH];
/* Delete all backups. */
backup_list = catalog_get_backup_list(INVALID_BACKUP_ID);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *) parray_get(backup_list, i);
pgBackupDeleteFiles(backup);
}
/* Cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
/* Delete all wal files. */
delete_walfiles(InvalidXLogRecPtr, 0);
/* Delete backup instance config file */
join_path_components(instance_config_path, backup_instance_path, BACKUP_CATALOG_CONF_FILE);
if (remove(instance_config_path))
{
elog(ERROR, "can't remove \"%s\": %s", instance_config_path,
strerror(errno));
}
/* Delete instance root directories */
if (rmdir(backup_instance_path) != 0)
elog(ERROR, "can't remove \"%s\": %s", backup_instance_path,
strerror(errno));
if (rmdir(arclog_path) != 0)
elog(ERROR, "can't remove \"%s\": %s", backup_instance_path,
strerror(errno));
elog(INFO, "Instance '%s' deleted successfully", instance_name);
return 0;
}

2
dir.c
View File

@ -307,7 +307,7 @@ dir_list_file(parray *files, const char *root, bool exclude, bool omit_symlink,
parray *black_list = NULL;
char path[MAXPGPATH];
join_path_components(path, backup_path, PG_BLACK_LIST);
join_path_components(path, backup_instance_path, PG_BLACK_LIST);
/* List files with black list */
if (root && pgdata && strcmp(root, pgdata) == 0 && fileExists(path))
{

50
help.c
View File

@ -16,6 +16,8 @@ static void help_show(void);
static void help_delete(void);
static void help_set_config(void);
static void help_show_config(void);
static void help_add_instance(void);
static void help_del_instance(void);
void
help_command(char *command)
@ -36,6 +38,10 @@ help_command(char *command)
help_set_config();
else if (strcmp(command, "show-config") == 0)
help_show_config();
else if (strcmp(command, "add-instance") == 0)
help_add_instance();
else if (strcmp(command, "del-instance") == 0)
help_del_instance();
else if (strcmp(command, "--help") == 0
|| strcmp(command, "help") == 0
|| strcmp(command, "-?") == 0
@ -94,6 +100,12 @@ help_pg_probackup(void)
printf(_("\n %s delete -B backup-dir\n"), PROGRAM_NAME);
printf(_(" [--wal] [-i backup-id | --expired]\n"));
printf(_("\n %s add-instance -B backup-dir\n"), PROGRAM_NAME);
printf(_(" --instance=instance_name\n"));
printf(_("\n %s del-instance -B backup-dir\n"), PROGRAM_NAME);
printf(_(" --instance=instance_name\n"));
if ((PROGRAM_URL || PROGRAM_EMAIL))
{
printf("\n");
@ -116,7 +128,7 @@ help_init(void)
static void
help_backup(void)
{
printf(_("%s backup -B backup-path -b backup-mode\n"), PROGRAM_NAME);
printf(_("%s backup -B backup-path -b backup-mode --instance=instance_name\n"), PROGRAM_NAME);
printf(_(" [-D pgdata-dir] [-C] [--stream [-S slot-name]] [--backup-pg-log]\n"));
printf(_(" [-j num-threads] [--archive-timeout=archive-timeout]\n"));
printf(_(" [--progress] [--delete-expired]\n"));
@ -124,6 +136,7 @@ help_backup(void)
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" -b, --backup-mode=backup-mode backup mode=FULL|PAGE|PTRACK\n"));
printf(_(" --instance=instance_name name of the instance\n"));
printf(_(" -D, --pgdata=pgdata-dir location of the database storage area\n"));
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
printf(_(" --stream stream the transaction log and include it in the backup\n"));
@ -166,12 +179,13 @@ help_restore(void)
static void
help_validate(void)
{
printf(_("%s validate -B backup-dir\n"), PROGRAM_NAME);
printf(_("%s validate -B backup-dir --instance=instance_name\n"), PROGRAM_NAME);
printf(_(" [-D pgdata-dir] [-i backup-id] [--progress]\n"));
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
printf(_(" [--timeline=timeline]\n\n"));
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name name of the instance\n"));
printf(_(" -D, --pgdata=pgdata-dir location of the database storage area\n"));
printf(_(" -i, --backup-id=backup-id backup to validate\n"));
@ -186,19 +200,21 @@ static void
help_show(void)
{
printf(_("%s show -B backup-dir\n"), PROGRAM_NAME);
printf(_(" [-i backup-id]\n\n"));
printf(_(" [--instance=instance_name] [-i backup-id]\n\n"));
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name show info about specific intstance\n"));
printf(_(" -i, --backup-id=backup-id show info about specific backups\n"));
}
static void
help_delete(void)
{
printf(_("%s delete -B backup-dir\n"), PROGRAM_NAME);
printf(_("%s delete -B backup-dir --instance=instance_name\n"), PROGRAM_NAME);
printf(_(" [--wal] [-i backup-id | --expired]\n\n"));
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name name of the instance\n"));
printf(_(" --wal remove unnecessary wal files\n"));
printf(_(" -i, --backup-id=backup-id backup to delete\n"));
printf(_(" --expired delete backups expired according to current\n"));
@ -208,7 +224,7 @@ help_delete(void)
static void
help_set_config(void)
{
printf(_("%s set-config -B backup-dir\n"), PROGRAM_NAME);
printf(_("%s set-config -B backup-dir --instance=instance_name\n"), PROGRAM_NAME);
printf(_(" [-d dbname] [-h host] [-p port] [-U username]\n"));
printf(_(" [--log-level=log-level]\n"));
printf(_(" [--log-filename=log-filename]\n"));
@ -220,6 +236,7 @@ help_set_config(void)
printf(_(" [--retention-window=retention-window]\n\n"));
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name name of the instance\n"));
printf(_("\n Connection options:\n"));
printf(_(" -d, --dbname=DBNAME database to connect\n"));
@ -250,7 +267,28 @@ help_set_config(void)
static void
help_show_config(void)
{
printf(_("%s show-config -B backup-dir\n\n"), PROGRAM_NAME);
printf(_("%s show-config -B backup-dir --instance=instance_name\n\n"), PROGRAM_NAME);
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name name of the instance\n"));
}
static void
help_add_instance(void)
{
printf(_("%s add-instance -B backup-dir\n"), PROGRAM_NAME);
printf(_(" --instance=instance_name\n\n"));
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name name of the new instance\n"));
}
static void
help_del_instance(void)
{
printf(_("%s del-instance -B backup-dir\n"), PROGRAM_NAME);
printf(_(" --instance=instance_name\n\n"));
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
printf(_(" --instance=instance_name name of the instance to delete\n"));
}

60
init.c
View File

@ -12,6 +12,7 @@
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
/*
* selects function for scandir.
@ -30,15 +31,8 @@ do_init(void)
{
char path[MAXPGPATH];
char arclog_path_dir[MAXPGPATH];
struct dirent **dp;
int results;
pgBackupConfig *config = pgut_new(pgBackupConfig);
/* PGDATA is always required */
if (pgdata == NULL)
elog(ERROR, "Required parameter not specified: PGDATA "
"(-D, --pgdata)");
if (access(backup_path, F_OK) == 0)
{
@ -47,20 +41,62 @@ do_init(void)
elog(ERROR, "backup catalog already exist and it's not empty");
}
/* Read system_identifier from PGDATA */
system_identifier = get_system_identifier();
/* create backup catalog root directory */
dir_create_dir(backup_path, DIR_PERMISSION);
/* create directories for backup of online files */
/* create backup catalog data directory */
join_path_components(path, backup_path, BACKUPS_DIR);
dir_create_dir(path, DIR_PERMISSION);
/* Create "wal" directory */
/* create backup catalog wal directory */
join_path_components(arclog_path_dir, backup_path, "wal");
dir_create_dir(arclog_path_dir, DIR_PERMISSION);
return 0;
}
int
do_add_instance(void)
{
char path[MAXPGPATH];
char arclog_path_dir[MAXPGPATH];
struct stat st;
pgBackupConfig *config = pgut_new(pgBackupConfig);
/* PGDATA is always required */
if (pgdata == NULL)
elog(ERROR, "Required parameter not specified: PGDATA "
"(-D, --pgdata)");
/* Read system_identifier from PGDATA */
system_identifier = get_system_identifier(pgdata);
/* Ensure that all root directories already exist */
if (access(backup_path, F_OK) != 0)
elog(ERROR, "%s directory does not exist.", backup_path);
join_path_components(path, backup_path, BACKUPS_DIR);
if (access(path, F_OK) != 0)
elog(ERROR, "%s directory does not exist.", path);
join_path_components(arclog_path_dir, backup_path, "wal");
if (access(arclog_path_dir, F_OK) != 0)
elog(ERROR, "%s directory does not exist.", arclog_path_dir);
/* Create directory for data files of this specific instance */
if (stat(backup_instance_path, &st) == 0 && S_ISDIR(st.st_mode))
elog(ERROR, "instance '%s' already exists", backup_instance_path);
dir_create_dir(backup_instance_path, DIR_PERMISSION);
/*
* Create directory for wal files of this specific instance.
* Existence check is extra paranoid because if we don't have such a
* directory in data dir, we shouldn't have it in wal as well.
*/
if (stat(arclog_path, &st) == 0 && S_ISDIR(st.st_mode))
elog(ERROR, "arclog_path '%s' already exists", arclog_path);
dir_create_dir(arclog_path, DIR_PERMISSION);
/*
* Wite initial config. system-identifier and pgdata are set in
* init subcommand and will never be updated.

View File

@ -283,8 +283,8 @@ validate_wal(pgBackup *backup,
*/
if (backup->stream)
{
sprintf(backup_xlog_path, "%s/%s/%s/%s/%s",
backup_path, BACKUPS_DIR, base36enc(backup->start_time), DATABASE_DIR, PG_XLOG_DIR);
sprintf(backup_xlog_path, "/%s/%s/%s/%s",
backup_instance_path, base36enc(backup->start_time), DATABASE_DIR, PG_XLOG_DIR);
validate_backup_wal_from_start_to_stop(backup, backup_xlog_path, tli);
}

View File

@ -15,6 +15,7 @@
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
const char *PROGRAM_VERSION = "1.1.13";
const char *PROGRAM_URL = "https://github.com/postgrespro/pg_probackup";
@ -23,7 +24,18 @@ const char *PROGRAM_EMAIL = "https://github.com/postgrespro/pg_probackup/issues"
/* path configuration */
char *backup_path = NULL;
char *pgdata = 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 *instance_name;
/* directory configuration */
pgBackup current;
@ -57,6 +69,10 @@ static char *target_xid;
static char *target_inclusive;
static TimeLineID target_tli;
/* archive push */
static char *wal_file_path;
static char *wal_file_name;
static void opt_backup_mode(pgut_option *opt, const char *arg);
static void opt_log_level(pgut_option *opt, const char *arg);
@ -108,6 +124,10 @@ static pgut_option options[] =
{ 'B', 'w', "no-password", &prompt_password, SOURCE_CMDLINE },
/* other options */
{ 'U', 50, "system-identifier", &system_identifier, SOURCE_FILE_STRICT },
{ 's', 51, "instance", &instance_name, SOURCE_CMDLINE },
/* archive-push options */
{ 's', 60, "wal-file-path", &wal_file_path, SOURCE_CMDLINE },
{ 's', 61, "wal-file-name", &wal_file_name, SOURCE_CMDLINE },
{ 0 }
};
@ -131,7 +151,15 @@ main(int argc, char *argv[])
/* Parse subcommands and non-subcommand options */
if (argc > 1)
{
if (strcmp(argv[1], "init") == 0)
if (strcmp(argv[1], "archive-push") == 0)
backup_subcmd = ARCHIVE_PUSH;
else if (strcmp(argv[1], "archive-get") == 0)
backup_subcmd = ARCHIVE_GET;
else if (strcmp(argv[1], "add-instance") == 0)
backup_subcmd = ADD_INSTANCE;
else if (strcmp(argv[1], "del-instance") == 0)
backup_subcmd = DELETE_INSTANCE;
else if (strcmp(argv[1], "init") == 0)
backup_subcmd = INIT;
else if (strcmp(argv[1], "backup") == 0)
backup_subcmd = BACKUP;
@ -180,36 +208,76 @@ main(int argc, char *argv[])
if (help)
help_command(argv[2]);
/* backup_path is required for all pg_probackup commands except help */
if (backup_path == NULL)
{
/* Try to read BACKUP_PATH from environment variable */
/*
* 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)");
}
/* Ensure that backup_path is an absolute path */
if (!is_absolute_path(backup_path))
elog(ERROR, "-B, --backup-path must be an absolute path");
/* Ensure that backup_path is a path to a directory */
rc = stat(backup_path, &stat_buf);
/* If rc == -1, there is no file or directory. So it's OK. */
if (rc != -1 && !S_ISDIR(stat_buf.st_mode))
elog(ERROR, "-B, --backup-path must be a path to directory");
/* Do not read options from file or env if we're going to set them */
if (backup_subcmd != SET_CONFIG)
/* Option --instance is required for all commands except init and show */
if (backup_subcmd != INIT && backup_subcmd != SHOW)
{
/* Read options from configuration file */
join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE);
pgut_readopt(path, options, ERROR);
/* Read environment variables */
pgut_getopt_env(options);
if (instance_name == NULL)
elog(ERROR, "required parameter not specified: --instance");
}
/*
* We read backup path from command line or from configuration file.
* Do final check.
* If --instance option was passed, construct paths for backup data and
* xlog files of this backup instance.
*/
if (!is_absolute_path(backup_path))
elog(ERROR, "-B, --backup-path must be an absolute path");
if (instance_name)
{
sprintf(backup_instance_path, "%s/%s/%s", backup_path, BACKUPS_DIR, instance_name);
sprintf(arclog_path, "%s/%s/%s", backup_path, "wal", instance_name);
/*
* Ensure that requested backup instance exists.
* for all commands except init, which doesn't take this parameter
* and add-instance which creates new instance.
*/
if (backup_subcmd != INIT && backup_subcmd != ADD_INSTANCE)
{
if (access(backup_instance_path, F_OK) != 0)
elog(ERROR, "Instance '%s' does not exist in this backup catalog",
instance_name);
}
}
/*
* Read options from env variables or from config file,
* unless we're going to set them via set-config.
*/
if (instance_name && backup_subcmd != SET_CONFIG)
{
/* Read environment variables */
pgut_getopt_env(options);
/* Read options from configuration file */
join_path_components(path, backup_instance_path, BACKUP_CATALOG_CONF_FILE);
pgut_readopt(path, options, ERROR);
}
/*
* We have read pgdata path from command line or from configuration file.
* Ensure that pgdata is an absolute path.
*/
if (pgdata != NULL && !is_absolute_path(pgdata))
elog(ERROR, "-D, --pgdata must be an absolute path");
/* Set log path */
if (log_filename || error_log_filename)
@ -220,8 +288,16 @@ main(int argc, char *argv[])
join_path_components(log_path, backup_path, "log");
}
/* Sanity check of --backup-id option */
if (backup_id_string_param != NULL)
{
if (backup_subcmd != RESTORE
&& backup_subcmd != VALIDATE
&& backup_subcmd != DELETE
&& backup_subcmd != SHOW)
elog(ERROR, "Cannot use -i (--backup-id) option together with the '%s' command",
argv[1]);
current.backup_id = base36dec(backup_id_string_param);
if (current.backup_id == 0)
elog(ERROR, "Invalid backup-id");
@ -237,15 +313,6 @@ main(int argc, char *argv[])
if (username != NULL)
dbuser = pstrdup(username);
/*
* We read pgdata path from command line or from configuration file.
* Do final check.
*/
if (pgdata != NULL && !is_absolute_path(pgdata))
elog(ERROR, "-D, --pgdata must be an absolute path");
join_path_components(arclog_path, backup_path, "wal");
/* setup exclusion list for file search */
if (!backup_logs)
{
@ -266,6 +333,14 @@ main(int argc, char *argv[])
/* do actual operation */
switch (backup_subcmd)
{
case ARCHIVE_PUSH:
return do_archive_push(wal_file_path, wal_file_name);
case ARCHIVE_GET:
return do_archive_get(wal_file_path, wal_file_name);
case ADD_INSTANCE:
return do_add_instance();
case DELETE_INSTANCE:
return do_delete_instance();
case INIT:
return do_init();
case BACKUP:
@ -290,11 +365,11 @@ main(int argc, char *argv[])
else
return do_delete(current.backup_id);
case SHOW_CONFIG:
if (argc > 4)
if (argc > 5)
elog(ERROR, "show-config command doesn't accept any options");
return do_configure(true);
case SET_CONFIG:
if (argc == 4)
if (argc == 5)
elog(ERROR, "set-config command requires at least one option");
return do_configure(false);
}

View File

@ -105,6 +105,10 @@ typedef enum BackupMode
typedef enum ProbackupSubcmd
{
INIT = 0,
ARCHIVE_PUSH,
ARCHIVE_GET,
ADD_INSTANCE,
DELETE_INSTANCE,
BACKUP,
RESTORE,
VALIDATE,
@ -231,9 +235,12 @@ extern int cfs_munmap(FileMap* map);
/* path configuration */
extern char *backup_path;
extern char backup_instance_path[MAXPGPATH];
extern char *pgdata;
extern char arclog_path[MAXPGPATH];
extern char *instance_name;
/* current settings */
extern pgBackup current;
extern ProbackupSubcmd backup_subcmd;
@ -285,6 +292,12 @@ extern void opt_tablespace_map(pgut_option *opt, const char *arg);
/* in init.c */
extern int do_init(void);
extern int do_add_instance(void);
/* in archive.c */
extern int do_archive_push(char *wal_file_path, char *wal_file_name);
extern int do_archive_get(char *wal_file_path, char *wal_file_name);
/* in configure.c */
extern int do_configure(bool show_only);
@ -299,6 +312,7 @@ extern int do_show(time_t requested_backup_id);
/* in delete.c */
extern int do_delete(time_t backup_id);
extern int do_retention_purge(void);
extern int do_delete_instance(void);
/* in fetch.c */
extern char *slurpFile(const char *datadir,
@ -366,6 +380,7 @@ extern bool backup_compressed_file_partially(pgFile *file,
size_t *skip_size);
extern bool copy_file(const char *from_root, const char *to_root,
pgFile *file);
extern void copy_wal_file(const char *from_root, const char *to_root);
extern bool copy_file_partly(const char *from_root, const char *to_root,
pgFile *file, size_t skip_size);
@ -399,7 +414,7 @@ extern XLogRecPtr get_last_ptrack_lsn(void);
extern uint32 get_data_checksum_version(bool safe);
extern char *base36enc(long unsigned int value);
extern long unsigned int base36dec(const char *text);
extern uint64 get_system_identifier(void);
extern uint64 get_system_identifier(char *pgdata);
extern pg_time_t timestamptz_to_time_t(TimestampTz t);
extern void pgBackup_init(pgBackup *backup);

View File

@ -271,7 +271,7 @@ void
restore_backup(pgBackup *backup)
{
char timestamp[100];
char backup_path[MAXPGPATH];
char this_backup_path[MAXPGPATH];
char database_path[MAXPGPATH];
char list_path[MAXPGPATH];
parray *files;
@ -298,9 +298,10 @@ restore_backup(pgBackup *backup)
/*
* Restore backup directories.
* this_backup_path = $BACKUP_PATH/backups/instance_name/backup_id
*/
pgBackupGetPath(backup, backup_path, lengthof(backup_path), NULL);
restore_directories(pgdata, backup_path);
pgBackupGetPath(backup, this_backup_path, lengthof(this_backup_path), NULL);
restore_directories(pgdata, this_backup_path);
/*
* Get list of files which need to be restored.
@ -561,7 +562,7 @@ create_directory:
static void
check_tablespace_mapping(pgBackup *backup)
{
char backup_path[MAXPGPATH];
char this_backup_path[MAXPGPATH];
parray *links;
size_t i;
TablespaceListCell *cell;
@ -569,8 +570,8 @@ check_tablespace_mapping(pgBackup *backup)
links = parray_new();
pgBackupGetPath(backup, backup_path, lengthof(backup_path), NULL);
read_tablespace_map(links, backup_path);
pgBackupGetPath(backup, this_backup_path, lengthof(this_backup_path), NULL);
read_tablespace_map(links, this_backup_path);
elog(LOG, "check tablespace directories of backup %s", base36enc(backup->start_time));
@ -704,7 +705,8 @@ create_recovery_conf(time_t backup_id,
fprintf(fp, "# recovery.conf generated by pg_probackup %s\n",
PROGRAM_VERSION);
fprintf(fp, "restore_command = 'cp %s/%%f %%p'\n", arclog_path);
fprintf(fp, "restore_command = 'pg_probackup archive-get -B %s --instance %s --wal-file-path %%p --wal-file-name %%f'\n",
backup_path, instance_name);
if (target_time)
fprintf(fp, "recovery_target_time = '%s'\n", target_time);

65
show.c
View File

@ -10,19 +10,70 @@
#include "pg_probackup.h"
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
static void show_backup_list(FILE *out, parray *backup_list);
static void show_backup_detail(FILE *out, pgBackup *backup);
/*
* If 'requested_backup_id' is INVALID_BACKUP_ID, show brief meta information
* about all backups in the backup catalog.
* If valid backup id is passed, show detailed meta information
* about specified backup.
*/
static int do_show_instance(time_t requested_backup_id);
int
do_show(time_t requested_backup_id)
{
if (instance_name == NULL)
{
/* Show list of instances */
char path[MAXPGPATH];
DIR *dir;
struct dirent *dent;
/* open directory and list contents */
join_path_components(path, backup_path, BACKUPS_DIR);
dir = opendir(path);
if (dir == NULL)
elog(ERROR, "cannot open directory \"%s\": %s", path, strerror(errno));
errno = 0;
while ((dent = readdir(dir)))
{
char child[MAXPGPATH];
struct stat st;
/* skip entries point current dir or parent dir */
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
join_path_components(child, path, dent->d_name);
if (lstat(child, &st) == -1)
elog(ERROR, "cannot stat file \"%s\": %s", child, strerror(errno));
if (!S_ISDIR(st.st_mode))
continue;
instance_name = dent->d_name;
sprintf(backup_instance_path, "%s/%s/%s", backup_path, BACKUPS_DIR, instance_name);
fprintf(stdout, "\nBACKUP INSTANCE '%s'\n", instance_name);
do_show_instance(0);
}
return 0;
}
else
return do_show_instance(requested_backup_id);
}
/*
* If 'requested_backup_id' is INVALID_BACKUP_ID, show brief meta information
* about all backups in the backup instance.
* If valid backup id is passed, show detailed meta information
* about specified backup.
*/
static int
do_show_instance(time_t requested_backup_id)
{
if (requested_backup_id != INVALID_BACKUP_ID)
{

4
util.c
View File

@ -114,14 +114,14 @@ get_current_timeline(bool safe)
}
uint64
get_system_identifier(void)
get_system_identifier(char *pgdata_path)
{
ControlFileData ControlFile;
char *buffer;
size_t size;
/* First fetch file... */
buffer = slurpFile(pgdata, "global/pg_control", &size, false);
buffer = slurpFile(pgdata_path, "global/pg_control", &size, false);
if (buffer == NULL)
return 0;
digestControlFile(&ControlFile, buffer, size);