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:
parent
5390887107
commit
4e94454544
1
Makefile
1
Makefile
@ -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
88
archive.c
Normal 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;
|
||||
}
|
2
backup.c
2
backup.c
@ -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()",
|
||||
|
22
catalog.c
22
catalog.c
@ -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);
|
||||
|
@ -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
99
data.c
@ -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
|
||||
|
45
delete.c
45
delete.c
@ -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
2
dir.c
@ -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
50
help.c
@ -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
60
init.c
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
127
pg_probackup.c
127
pg_probackup.c
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
16
restore.c
16
restore.c
@ -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
65
show.c
@ -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
4
util.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user