2016-11-16 19:47:12 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_probackup.c: Backup/Recovery manager for PostgreSQL.
|
|
|
|
*
|
2017-03-01 15:50:07 +02:00
|
|
|
* Portions Copyright (c) 2009-2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
|
|
|
* Portions Copyright (c) 2015-2017, Postgres Professional
|
2016-11-16 19:47:12 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "pg_probackup.h"
|
|
|
|
#include "streamutil.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2017-04-07 14:15:41 +02:00
|
|
|
const char *PROGRAM_VERSION = "1.1.5";
|
2016-11-16 19:47:12 +02:00
|
|
|
const char *PROGRAM_URL = "https://github.com/postgrespro/pg_probackup";
|
|
|
|
const char *PROGRAM_EMAIL = "https://github.com/postgrespro/pg_probackup/issues";
|
|
|
|
|
|
|
|
/* path configuration */
|
|
|
|
char *backup_path;
|
|
|
|
char *pgdata;
|
|
|
|
char arclog_path[MAXPGPATH];
|
|
|
|
|
2017-04-12 18:13:43 +02:00
|
|
|
char *backup_id_string_param = NULL;
|
2016-11-16 19:47:12 +02:00
|
|
|
/* directory configuration */
|
|
|
|
pgBackup current;
|
|
|
|
|
|
|
|
/* backup configuration */
|
|
|
|
static bool smooth_checkpoint;
|
|
|
|
int num_threads = 1;
|
|
|
|
bool stream_wal = false;
|
|
|
|
bool from_replica = false;
|
|
|
|
static bool backup_logs = false;
|
|
|
|
bool progress = false;
|
2017-04-12 18:32:05 +02:00
|
|
|
/* delete options */
|
2016-11-16 19:47:12 +02:00
|
|
|
bool delete_wal = false;
|
2017-04-12 18:32:05 +02:00
|
|
|
bool delete_expired = false;
|
|
|
|
bool apply_to_all = false;
|
|
|
|
bool force_delete = false;
|
2016-11-16 19:47:12 +02:00
|
|
|
|
|
|
|
/* restore configuration */
|
|
|
|
static char *target_time;
|
|
|
|
static char *target_xid;
|
|
|
|
static char *target_inclusive;
|
|
|
|
static TimeLineID target_tli;
|
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
uint64 system_identifier = 0;
|
|
|
|
|
|
|
|
/* retention configuration */
|
|
|
|
uint32 retention_redundancy = 0;
|
|
|
|
uint32 retention_window = 0;
|
|
|
|
|
2016-11-16 19:47:12 +02:00
|
|
|
static void opt_backup_mode(pgut_option *opt, const char *arg);
|
|
|
|
|
|
|
|
static pgut_option options[] =
|
|
|
|
{
|
|
|
|
/* directory options */
|
2017-02-12 22:42:10 +02:00
|
|
|
{ 's', 'D', "pgdata", &pgdata, SOURCE_CMDLINE },
|
|
|
|
{ 's', 'B', "backup-path", &backup_path, SOURCE_CMDLINE },
|
2016-11-16 19:47:12 +02:00
|
|
|
/* common options */
|
2017-02-12 22:42:10 +02:00
|
|
|
{ 'u', 'j', "threads", &num_threads, SOURCE_CMDLINE },
|
|
|
|
{ 'b', 8, "stream", &stream_wal, SOURCE_CMDLINE },
|
|
|
|
{ 'b', 11, "progress", &progress, SOURCE_CMDLINE },
|
2017-04-12 18:13:43 +02:00
|
|
|
{ 's', 'i', "backup-id", &backup_id_string_param, SOURCE_CMDLINE },
|
2016-11-16 19:47:12 +02:00
|
|
|
/* backup options */
|
2017-02-12 22:42:10 +02:00
|
|
|
{ 'b', 10, "backup-pg-log", &backup_logs, SOURCE_CMDLINE },
|
2017-04-12 18:32:05 +02:00
|
|
|
{ 'f', 'b', "backup-mode", opt_backup_mode, SOURCE_CMDLINE },
|
|
|
|
{ 'b', 'C', "smooth-checkpoint", &smooth_checkpoint, SOURCE_CMDLINE },
|
|
|
|
{ 's', 'S', "slot", &replication_slot, SOURCE_CMDLINE },
|
2017-03-24 13:07:03 +02:00
|
|
|
{ 'u', 2, "archive-timeout", &archive_timeout, SOURCE_CMDLINE },
|
2017-04-12 18:32:05 +02:00
|
|
|
{ 'b', 19, "delete-expired", &delete_expired, SOURCE_CMDLINE },
|
2016-11-16 19:47:12 +02:00
|
|
|
/* restore options */
|
2016-11-17 12:11:24 +02:00
|
|
|
{ 's', 3, "time", &target_time, SOURCE_CMDLINE },
|
|
|
|
{ 's', 4, "xid", &target_xid, SOURCE_CMDLINE },
|
|
|
|
{ 's', 5, "inclusive", &target_inclusive, SOURCE_CMDLINE },
|
|
|
|
{ 'u', 6, "timeline", &target_tli, SOURCE_CMDLINE },
|
2017-02-25 14:12:07 +02:00
|
|
|
{ 'f', 'T', "tablespace-mapping", opt_tablespace_map, SOURCE_CMDLINE },
|
2016-11-16 19:47:12 +02:00
|
|
|
/* delete options */
|
2017-02-15 15:22:49 +02:00
|
|
|
{ 'b', 12, "wal", &delete_wal, SOURCE_CMDLINE },
|
2017-04-12 18:32:05 +02:00
|
|
|
{ 'b', 16, "expired", &delete_expired, SOURCE_CMDLINE },
|
|
|
|
{ 'b', 17, "all", &apply_to_all, SOURCE_CMDLINE },
|
|
|
|
{ 'b', 18, "force", &force_delete, SOURCE_CMDLINE },
|
|
|
|
/* configure options */
|
|
|
|
{ 'u', 13, "set-retention-redundancy", &retention_redundancy, SOURCE_CMDLINE },
|
|
|
|
{ 'u', 14, "set-retention-window", &retention_window, SOURCE_CMDLINE },
|
2016-11-25 16:50:54 +02:00
|
|
|
/* other */
|
2017-02-12 22:42:10 +02:00
|
|
|
{ 'U', 15, "system-identifier", &system_identifier, SOURCE_FILE_STRICT },
|
2017-04-12 16:26:19 +02:00
|
|
|
|
|
|
|
{ 's', 'd', "dbname" , &pgut_dbname, SOURCE_CMDLINE },
|
|
|
|
{ 's', 'h', "host" , &host, SOURCE_CMDLINE },
|
|
|
|
{ 's', 'p', "port" , &port, SOURCE_CMDLINE },
|
|
|
|
{ 's', 'U', "username" , &username, SOURCE_CMDLINE },
|
2017-04-12 17:57:02 +02:00
|
|
|
{ 'b', 'q', "quiet" , &quiet, SOURCE_CMDLINE },
|
2017-04-12 16:26:19 +02:00
|
|
|
{ 'b', 'v', "verbose" , &verbose, SOURCE_CMDLINE },
|
|
|
|
{ 'B', 'w', "no-password" , &prompt_password, SOURCE_CMDLINE },
|
2016-11-16 19:47:12 +02:00
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Entry point of pg_probackup command.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
2017-04-12 16:26:19 +02:00
|
|
|
ProbackupSubcmd backup_subcmd;
|
2016-11-16 19:47:12 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* initialize configuration */
|
2017-02-12 22:42:10 +02:00
|
|
|
init_backup(¤t);
|
2016-11-16 19:47:12 +02:00
|
|
|
|
2017-04-12 16:26:19 +02:00
|
|
|
PROGRAM_NAME = get_progname(argv[0]);
|
|
|
|
set_pglocale_pgservice(argv[0], "pgscripts");
|
2016-11-16 19:47:12 +02:00
|
|
|
|
2017-04-12 16:26:19 +02:00
|
|
|
/* Parse subcommands and non-subcommand options */
|
|
|
|
if (argc > 1)
|
2016-11-16 19:47:12 +02:00
|
|
|
{
|
2017-04-12 16:26:19 +02:00
|
|
|
if (strcmp(argv[1], "init") == 0)
|
|
|
|
backup_subcmd = INIT;
|
|
|
|
else if (strcmp(argv[1], "backup") == 0)
|
|
|
|
backup_subcmd = BACKUP;
|
|
|
|
else if (strcmp(argv[1], "restore") == 0)
|
|
|
|
backup_subcmd = RESTORE;
|
|
|
|
else if (strcmp(argv[1], "validate") == 0)
|
|
|
|
backup_subcmd = VALIDATE;
|
|
|
|
else if (strcmp(argv[1], "show") == 0)
|
|
|
|
backup_subcmd = SHOW;
|
|
|
|
else if (strcmp(argv[1], "delete") == 0)
|
|
|
|
backup_subcmd = DELETE;
|
|
|
|
else if (strcmp(argv[1], "configure") == 0)
|
|
|
|
backup_subcmd = CONFIGURE;
|
|
|
|
else if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
|
|
|
|
{
|
|
|
|
help(true);
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
else if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s %s\n", PROGRAM_NAME, PROGRAM_VERSION);
|
|
|
|
exit(0);
|
|
|
|
}
|
2016-11-16 19:47:12 +02:00
|
|
|
else
|
2017-04-12 16:26:19 +02:00
|
|
|
elog(ERROR, "Invalid subcommand");
|
2016-11-16 19:47:12 +02:00
|
|
|
}
|
|
|
|
|
2017-04-12 16:26:19 +02:00
|
|
|
/* Parse command line arguments */
|
|
|
|
i = pgut_getopt(argc, argv, options);
|
2016-11-16 19:47:12 +02:00
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
/* BACKUP_PATH is always required */
|
|
|
|
if (backup_path == NULL)
|
|
|
|
elog(ERROR, "required parameter not specified: BACKUP_PATH (-B, --backup-path)");
|
|
|
|
else
|
2016-11-16 19:47:12 +02:00
|
|
|
{
|
2017-02-12 22:42:10 +02:00
|
|
|
char path[MAXPGPATH];
|
2016-11-16 19:47:12 +02:00
|
|
|
/* Check if backup_path is directory. */
|
|
|
|
struct stat stat_buf;
|
2017-02-12 22:42:10 +02:00
|
|
|
int rc = stat(backup_path, &stat_buf);
|
2016-11-16 19:47:12 +02:00
|
|
|
|
|
|
|
/* 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");
|
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
join_path_components(path, backup_path, BACKUP_CATALOG_CONF_FILE);
|
2016-11-16 19:47:12 +02:00
|
|
|
pgut_readopt(path, options, ERROR);
|
|
|
|
}
|
|
|
|
|
2017-04-12 18:13:43 +02:00
|
|
|
if (backup_id_string_param != NULL)
|
|
|
|
{
|
2017-04-13 18:37:29 +02:00
|
|
|
current.backup_id = base36dec(backup_id_string_param);
|
|
|
|
if (current.backup_id == 0)
|
2017-04-12 18:13:43 +02:00
|
|
|
elog(ERROR, "Invalid backup-id");
|
|
|
|
}
|
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
/* setup stream options */
|
|
|
|
if (pgut_dbname != NULL)
|
|
|
|
dbname = pstrdup(pgut_dbname);
|
|
|
|
if (host != NULL)
|
|
|
|
dbhost = pstrdup(host);
|
|
|
|
if (port != NULL)
|
|
|
|
dbport = pstrdup(port);
|
|
|
|
if (username != NULL)
|
|
|
|
dbuser = pstrdup(username);
|
2016-11-16 19:47:12 +02:00
|
|
|
|
|
|
|
/* path must be absolute */
|
2017-02-12 22:42:10 +02:00
|
|
|
if (!is_absolute_path(backup_path))
|
2016-11-16 19:47:12 +02:00
|
|
|
elog(ERROR, "-B, --backup-path must be an absolute path");
|
|
|
|
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 */
|
2017-02-13 13:11:00 +02:00
|
|
|
for (i = 0; pgdata_exclude_dir[i]; i++); /* find first empty slot */
|
2016-11-16 19:47:12 +02:00
|
|
|
|
|
|
|
if(!backup_logs)
|
2017-02-13 13:11:00 +02:00
|
|
|
pgdata_exclude_dir[i++] = "pg_log";
|
2016-11-16 19:47:12 +02:00
|
|
|
|
|
|
|
if (target_time != NULL && target_xid != NULL)
|
|
|
|
elog(ERROR, "You can't specify recovery-target-time and recovery-target-xid at the same time");
|
|
|
|
|
2016-11-25 13:26:58 +02:00
|
|
|
if (num_threads < 1)
|
|
|
|
num_threads = 1;
|
|
|
|
|
2016-11-16 19:47:12 +02:00
|
|
|
/* do actual operation */
|
2017-04-12 16:26:19 +02:00
|
|
|
switch (backup_subcmd)
|
|
|
|
{
|
|
|
|
case INIT:
|
|
|
|
return do_init();
|
|
|
|
case BACKUP:
|
|
|
|
return do_backup(smooth_checkpoint);
|
|
|
|
case RESTORE:
|
2017-04-13 18:37:29 +02:00
|
|
|
return do_restore_or_validate(current.backup_id,
|
|
|
|
target_time, target_xid,
|
|
|
|
target_inclusive, target_tli,
|
|
|
|
true);
|
2017-04-12 16:26:19 +02:00
|
|
|
case VALIDATE:
|
2017-04-13 18:37:29 +02:00
|
|
|
return do_restore_or_validate(current.backup_id,
|
|
|
|
target_time, target_xid,
|
|
|
|
target_inclusive, target_tli,
|
|
|
|
false);
|
2017-04-12 16:26:19 +02:00
|
|
|
case SHOW:
|
2017-04-13 18:37:29 +02:00
|
|
|
return do_show(current.backup_id);
|
2017-04-12 16:26:19 +02:00
|
|
|
case DELETE:
|
2017-04-13 18:37:29 +02:00
|
|
|
return do_delete(current.backup_id);
|
2017-04-12 16:26:19 +02:00
|
|
|
case CONFIGURE:
|
|
|
|
elog(ERROR, "not implemented yet");
|
2017-02-12 22:42:10 +02:00
|
|
|
}
|
2017-04-12 16:26:19 +02:00
|
|
|
|
|
|
|
// if (pg_strcasecmp(cmd, "retention") == 0)
|
|
|
|
// {
|
|
|
|
// if (subcmd == NULL)
|
|
|
|
// elog(ERROR, "you must specify retention command");
|
|
|
|
// else if (pg_strcasecmp(subcmd, "show") == 0)
|
|
|
|
// return do_retention_show();
|
|
|
|
// else if (pg_strcasecmp(subcmd, "purge") == 0)
|
|
|
|
// return do_retention_purge();
|
|
|
|
// }
|
2016-11-16 19:47:12 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pgut_help(bool details)
|
|
|
|
{
|
|
|
|
printf(_("%s manage backup/recovery of PostgreSQL database.\n\n"), PROGRAM_NAME);
|
|
|
|
printf(_("Usage:\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" %s [option...] init\n"), PROGRAM_NAME);
|
|
|
|
printf(_(" %s [option...] backup\n"), PROGRAM_NAME);
|
2017-02-12 22:42:10 +02:00
|
|
|
printf(_(" %s [option...] restore [backup-ID]\n"), PROGRAM_NAME);
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" %s [option...] show [backup-ID]\n"), PROGRAM_NAME);
|
2017-02-16 18:44:16 +02:00
|
|
|
printf(_(" %s [option...] validate [backup-ID]\n"), PROGRAM_NAME);
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" %s [option...] delete backup-ID\n"), PROGRAM_NAME);
|
|
|
|
printf(_(" %s [option...] delwal [backup-ID]\n"), PROGRAM_NAME);
|
2017-02-12 22:42:10 +02:00
|
|
|
printf(_(" %s [option...] retention show|purge\n"), PROGRAM_NAME);
|
2016-11-16 19:47:12 +02:00
|
|
|
|
|
|
|
if (!details)
|
|
|
|
return;
|
|
|
|
|
|
|
|
printf(_("\nCommon Options:\n"));
|
|
|
|
printf(_(" -B, --backup-path=PATH location of the backup storage area\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" -D, --pgdata=PATH location of the database storage area\n"));
|
2016-11-16 19:47:12 +02:00
|
|
|
/*printf(_(" -c, --check show what would have been done\n"));*/
|
|
|
|
printf(_("\nBackup options:\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" -b, --backup-mode=MODE backup mode (full, page, ptrack)\n"));
|
2016-11-16 19:47:12 +02:00
|
|
|
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" --stream stream the transaction log and include it in the backup\n"));
|
2017-03-24 13:07:03 +02:00
|
|
|
printf(_(" --archive-timeout wait timeout for WAL segment archiving\n"));
|
2016-11-16 19:47:12 +02:00
|
|
|
printf(_(" -S, --slot=SLOTNAME replication slot to use\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" --backup-pg-log backup of pg_log directory\n"));
|
|
|
|
printf(_(" -j, --threads=NUM number of parallel threads\n"));
|
|
|
|
printf(_(" --progress show progress\n"));
|
2016-11-16 19:47:12 +02:00
|
|
|
printf(_("\nRestore options:\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" --time time stamp up to which recovery will proceed\n"));
|
|
|
|
printf(_(" --xid transaction ID up to which recovery will proceed\n"));
|
|
|
|
printf(_(" --inclusive whether we stop just after the recovery target\n"));
|
|
|
|
printf(_(" --timeline recovering into a particular timeline\n"));
|
2017-02-25 14:12:07 +02:00
|
|
|
printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"));
|
|
|
|
printf(_(" relocate the tablespace in directory OLDDIR to NEWDIR\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" -j, --threads=NUM number of parallel threads\n"));
|
|
|
|
printf(_(" --progress show progress\n"));
|
2016-11-16 19:47:12 +02:00
|
|
|
printf(_("\nDelete options:\n"));
|
2016-11-16 20:32:26 +02:00
|
|
|
printf(_(" --wal remove unnecessary wal files\n"));
|
2017-02-12 22:42:10 +02:00
|
|
|
printf(_("\nRetention options:\n"));
|
|
|
|
printf(_(" --redundancy specifies how many full backups purge command should keep\n"));
|
|
|
|
printf(_(" --window specifies the number of days of recoverability\n"));
|
2016-11-16 19:47:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opt_backup_mode(pgut_option *opt, const char *arg)
|
|
|
|
{
|
|
|
|
current.backup_mode = parse_backup_mode(arg);
|
|
|
|
}
|