2011-12-26 17:21:58 +03:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
2014-01-27 06:02:56 +03:00
|
|
|
* pg_arman.c: Backup/Recovery manager for PostgreSQL.
|
2011-12-26 17:21:58 +03:00
|
|
|
*
|
2013-09-09 12:00:13 +03:00
|
|
|
* Copyright (c) 2009-2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
2011-12-26 17:21:58 +03:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2014-01-27 06:02:56 +03:00
|
|
|
#include "pg_arman.h"
|
2016-05-26 14:56:32 +02:00
|
|
|
#include "streamutil.h"
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2014-01-27 06:02:56 +03:00
|
|
|
const char *PROGRAM_VERSION = "0.1";
|
2016-10-13 15:20:53 +02:00
|
|
|
const char *PROGRAM_URL = "https://github.com/stalkerg/pg_arman";
|
|
|
|
const char *PROGRAM_EMAIL = "https://github.com/stalkerg/pg_arman/issues";
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
/* path configuration */
|
|
|
|
char *backup_path;
|
|
|
|
char *pgdata;
|
2016-10-18 19:22:53 +02:00
|
|
|
char arclog_path[MAXPGPATH];
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
/* common configuration */
|
|
|
|
bool check = false;
|
|
|
|
|
|
|
|
/* directory configuration */
|
|
|
|
pgBackup current;
|
|
|
|
|
|
|
|
/* backup configuration */
|
|
|
|
static bool smooth_checkpoint;
|
|
|
|
static int keep_data_generations = KEEP_INFINITE;
|
|
|
|
static int keep_data_days = KEEP_INFINITE;
|
2016-02-29 19:23:48 +02:00
|
|
|
int num_threads = 1;
|
2016-05-26 14:56:32 +02:00
|
|
|
bool stream_wal = false;
|
2016-09-29 16:33:21 +02:00
|
|
|
bool from_replica = false;
|
2016-09-02 17:31:06 +02:00
|
|
|
static bool backup_logs = false;
|
2016-09-02 19:16:00 +02:00
|
|
|
bool progress = false;
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
/* restore configuration */
|
|
|
|
static char *target_time;
|
|
|
|
static char *target_xid;
|
|
|
|
static char *target_inclusive;
|
|
|
|
static TimeLineID target_tli;
|
|
|
|
|
|
|
|
/* show configuration */
|
|
|
|
static bool show_all = false;
|
|
|
|
|
|
|
|
static void opt_backup_mode(pgut_option *opt, const char *arg);
|
|
|
|
|
|
|
|
static pgut_option options[] =
|
|
|
|
{
|
|
|
|
/* directory options */
|
2014-01-17 16:42:27 +03:00
|
|
|
{ 's', 'D', "pgdata", &pgdata, SOURCE_ENV },
|
|
|
|
{ 's', 'B', "backup-path", &backup_path, SOURCE_ENV },
|
2011-12-26 17:21:58 +03:00
|
|
|
/* common options */
|
2014-01-17 16:42:27 +03:00
|
|
|
{ 'b', 'c', "check", &check },
|
2016-02-29 21:00:57 +02:00
|
|
|
{ 'i', 'j', "threads", &num_threads },
|
2016-09-02 17:31:06 +02:00
|
|
|
{ 'b', 8, "stream", &stream_wal },
|
2016-09-02 19:16:00 +02:00
|
|
|
{ 'b', 11, "progress", &progress },
|
2011-12-26 17:21:58 +03:00
|
|
|
/* backup options */
|
2016-09-02 17:31:06 +02:00
|
|
|
{ 'b', 10, "backup-pg-log", &backup_logs },
|
2014-01-17 16:42:27 +03:00
|
|
|
{ 'f', 'b', "backup-mode", opt_backup_mode, SOURCE_ENV },
|
|
|
|
{ 'b', 'C', "smooth-checkpoint", &smooth_checkpoint, SOURCE_ENV },
|
2016-10-25 19:21:27 +02:00
|
|
|
{ 's', 'S', "slot", &replication_slot, SOURCE_ENV },
|
2011-12-26 17:21:58 +03:00
|
|
|
/* options with only long name (keep-xxx) */
|
2014-01-17 16:42:27 +03:00
|
|
|
{ 'i', 1, "keep-data-generations", &keep_data_generations, SOURCE_ENV },
|
|
|
|
{ 'i', 2, "keep-data-days", &keep_data_days, SOURCE_ENV },
|
2011-12-26 17:21:58 +03:00
|
|
|
/* restore options */
|
2014-01-24 16:36:31 +03:00
|
|
|
{ 's', 3, "recovery-target-time", &target_time, SOURCE_ENV },
|
|
|
|
{ 's', 4, "recovery-target-xid", &target_xid, SOURCE_ENV },
|
|
|
|
{ 's', 5, "recovery-target-inclusive", &target_inclusive, SOURCE_ENV },
|
|
|
|
{ 'u', 6, "recovery-target-timeline", &target_tli, SOURCE_ENV },
|
2011-12-26 17:21:58 +03:00
|
|
|
/* catalog options */
|
2014-01-17 16:42:27 +03:00
|
|
|
{ 'b', 'a', "show-all", &show_all },
|
2011-12-26 17:21:58 +03:00
|
|
|
{ 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2014-01-27 06:02:56 +03:00
|
|
|
* Entry point of pg_arman command.
|
2011-12-26 17:21:58 +03:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
const char *cmd = NULL;
|
2016-10-31 17:19:11 +02:00
|
|
|
const char *backup_id_string = NULL;
|
|
|
|
time_t backup_id = 0;
|
2011-12-26 17:21:58 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* do not buffer progress messages */
|
|
|
|
setvbuf(stdout, 0, _IONBF, 0); /* TODO: remove this */
|
|
|
|
|
|
|
|
/* initialize configuration */
|
|
|
|
catalog_init_config(¤t);
|
|
|
|
|
|
|
|
/* overwrite configuration with command line arguments */
|
|
|
|
i = pgut_getopt(argc, argv, options);
|
|
|
|
|
|
|
|
for (; i < argc; i++)
|
|
|
|
{
|
|
|
|
if (cmd == NULL)
|
2016-10-13 15:20:53 +02:00
|
|
|
{
|
2011-12-26 17:21:58 +03:00
|
|
|
cmd = argv[i];
|
2016-10-13 15:20:53 +02:00
|
|
|
if(strcmp(cmd, "show") != 0 &&
|
|
|
|
strcmp(cmd, "validate") != 0 &&
|
2016-11-02 20:04:24 +02:00
|
|
|
strcmp(cmd, "delete") != 0 &&
|
|
|
|
strcmp(cmd, "restore") != 0)
|
2016-10-13 15:20:53 +02:00
|
|
|
break;
|
2016-10-31 17:19:11 +02:00
|
|
|
} else if (backup_id_string == NULL)
|
|
|
|
backup_id_string = argv[i];
|
2011-12-26 17:21:58 +03:00
|
|
|
else
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "too many arguments");
|
2011-12-26 17:21:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* command argument (backup/restore/show/...) is required. */
|
|
|
|
if (cmd == NULL)
|
|
|
|
{
|
|
|
|
help(false);
|
2016-01-19 05:41:30 +02:00
|
|
|
return 1;
|
2011-12-26 17:21:58 +03:00
|
|
|
}
|
|
|
|
|
2016-10-31 17:19:11 +02:00
|
|
|
if (backup_id_string != NULL)
|
2016-11-02 20:04:24 +02:00
|
|
|
{
|
2016-10-31 17:19:11 +02:00
|
|
|
backup_id = base36dec(backup_id_string);
|
2016-11-02 20:04:24 +02:00
|
|
|
if (backup_id == 0) {
|
|
|
|
elog(ERROR, "wrong ID");
|
|
|
|
}
|
|
|
|
}
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
/* Read default configuration from file. */
|
|
|
|
if (backup_path)
|
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
/* Check if backup_path is directory. */
|
|
|
|
struct stat stat_buf;
|
|
|
|
int rc = stat(backup_path, &stat_buf);
|
2016-01-14 09:36:39 +02:00
|
|
|
|
|
|
|
/* If rc == -1, there is no file or directory. So it's OK. */
|
|
|
|
if (rc != -1 && !S_ISDIR(stat_buf.st_mode))
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "-B, --backup-path must be a path to directory");
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
join_path_components(path, backup_path, PG_RMAN_INI_FILE);
|
2016-01-19 05:41:30 +02:00
|
|
|
pgut_readopt(path, options, ERROR);
|
2016-05-26 14:56:32 +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);
|
2011-12-26 17:21:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* BACKUP_PATH is always required */
|
|
|
|
if (backup_path == NULL)
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "required parameter not specified: BACKUP_PATH (-B, --backup-path)");
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
/* path must be absolute */
|
|
|
|
if (backup_path != NULL && !is_absolute_path(backup_path))
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "-B, --backup-path must be an absolute path");
|
2011-12-26 17:21:58 +03:00
|
|
|
if (pgdata != NULL && !is_absolute_path(pgdata))
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "-D, --pgdata must be an absolute path");
|
2011-12-26 17:21:58 +03:00
|
|
|
|
2016-10-18 19:22:53 +02:00
|
|
|
join_path_components(arclog_path, backup_path, "wal");
|
2016-01-18 09:11:47 +02:00
|
|
|
|
2011-12-26 17:21:58 +03:00
|
|
|
/* setup exclusion list for file search */
|
2016-09-02 18:31:49 +02:00
|
|
|
for (i = 0; pgdata_exclude[i]; i++); /* find first empty slot */
|
|
|
|
|
2016-10-18 19:22:53 +02:00
|
|
|
pgdata_exclude[i++] = arclog_path;
|
2011-12-26 17:21:58 +03:00
|
|
|
|
2016-09-02 17:31:06 +02:00
|
|
|
if(!backup_logs)
|
|
|
|
pgdata_exclude[i++] = "pg_log";
|
|
|
|
|
2016-11-08 14:53:45 +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");
|
|
|
|
|
2011-12-26 17:21:58 +03:00
|
|
|
/* do actual operation */
|
|
|
|
if (pg_strcasecmp(cmd, "init") == 0)
|
|
|
|
return do_init();
|
|
|
|
else if (pg_strcasecmp(cmd, "backup") == 0)
|
2013-09-09 12:00:13 +03:00
|
|
|
{
|
|
|
|
pgBackupOption bkupopt;
|
2014-01-17 16:42:27 +03:00
|
|
|
int res;
|
|
|
|
bkupopt.smooth_checkpoint = smooth_checkpoint;
|
|
|
|
bkupopt.keep_data_generations = keep_data_generations;
|
|
|
|
bkupopt.keep_data_days = keep_data_days;
|
|
|
|
|
|
|
|
/* Do the backup */
|
|
|
|
res = do_backup(bkupopt);
|
|
|
|
if (res != 0)
|
|
|
|
return res;
|
|
|
|
|
2016-10-31 17:19:11 +02:00
|
|
|
do_validate(current.start_time);
|
2013-09-09 12:00:13 +03:00
|
|
|
}
|
2016-01-14 09:36:39 +02:00
|
|
|
else if (pg_strcasecmp(cmd, "restore") == 0)
|
2016-11-02 20:04:24 +02:00
|
|
|
return do_restore(backup_id, target_time, target_xid,
|
2014-01-24 16:36:31 +03:00
|
|
|
target_inclusive, target_tli);
|
2011-12-26 17:21:58 +03:00
|
|
|
else if (pg_strcasecmp(cmd, "show") == 0)
|
2016-10-31 17:19:11 +02:00
|
|
|
return do_show(backup_id, show_all);
|
2011-12-26 17:21:58 +03:00
|
|
|
else if (pg_strcasecmp(cmd, "validate") == 0)
|
2016-10-31 17:19:11 +02:00
|
|
|
return do_validate(backup_id);
|
2011-12-26 17:21:58 +03:00
|
|
|
else if (pg_strcasecmp(cmd, "delete") == 0)
|
2016-10-31 17:19:11 +02:00
|
|
|
return do_delete(backup_id);
|
2011-12-26 17:21:58 +03:00
|
|
|
else
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "invalid command \"%s\"", cmd);
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pgut_help(bool details)
|
|
|
|
{
|
|
|
|
printf(_("%s manage backup/recovery of PostgreSQL database.\n\n"), PROGRAM_NAME);
|
|
|
|
printf(_("Usage:\n"));
|
|
|
|
printf(_(" %s OPTION init\n"), PROGRAM_NAME);
|
|
|
|
printf(_(" %s OPTION backup\n"), PROGRAM_NAME);
|
|
|
|
printf(_(" %s OPTION restore\n"), PROGRAM_NAME);
|
2016-10-31 17:57:15 +02:00
|
|
|
printf(_(" %s OPTION show [ID]\n"), PROGRAM_NAME);
|
|
|
|
printf(_(" %s OPTION validate [ID]\n"), PROGRAM_NAME);
|
|
|
|
printf(_(" %s OPTION delete ID\n"), PROGRAM_NAME);
|
2011-12-26 17:21:58 +03:00
|
|
|
|
|
|
|
if (!details)
|
|
|
|
return;
|
|
|
|
|
|
|
|
printf(_("\nCommon Options:\n"));
|
|
|
|
printf(_(" -D, --pgdata=PATH location of the database storage area\n"));
|
|
|
|
printf(_(" -B, --backup-path=PATH location of the backup storage area\n"));
|
|
|
|
printf(_(" -c, --check show what would have been done\n"));
|
2016-02-29 21:00:57 +02:00
|
|
|
printf(_(" -j, --threads=NUM num threads for backup and restore\n"));
|
2016-09-02 19:16:00 +02:00
|
|
|
printf(_(" --progress show progress copy files\n"));
|
2011-12-26 17:21:58 +03:00
|
|
|
printf(_("\nBackup options:\n"));
|
2016-02-29 21:00:57 +02:00
|
|
|
printf(_(" -b, --backup-mode=MODE full,page,ptrack\n"));
|
2011-12-26 17:21:58 +03:00
|
|
|
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
|
2016-11-01 16:25:46 +02:00
|
|
|
printf(_(" --stream use stream for save/restore WAL during backup\n"));
|
2011-12-26 17:21:58 +03:00
|
|
|
printf(_(" --keep-data-generations=N keep GENERATION of full data backup\n"));
|
|
|
|
printf(_(" --keep-data-days=DAY keep enough data backup to recover to DAY days age\n"));
|
2016-09-02 17:31:06 +02:00
|
|
|
printf(_(" --backup-pg-log start backup pg_log directory\n"));
|
2016-10-25 19:21:27 +02:00
|
|
|
printf(_(" -S, --slot=SLOTNAME replication slot to use\n"));
|
2011-12-26 17:21:58 +03:00
|
|
|
printf(_("\nRestore options:\n"));
|
|
|
|
printf(_(" --recovery-target-time time stamp up to which recovery will proceed\n"));
|
|
|
|
printf(_(" --recovery-target-xid transaction ID up to which recovery will proceed\n"));
|
|
|
|
printf(_(" --recovery-target-inclusive whether we stop just after the recovery target\n"));
|
|
|
|
printf(_(" --recovery-target-timeline recovering into a particular timeline\n"));
|
|
|
|
printf(_("\nCatalog options:\n"));
|
|
|
|
printf(_(" -a, --show-all show deleted backup too\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
opt_backup_mode(pgut_option *opt, const char *arg)
|
|
|
|
{
|
2013-12-15 18:50:36 +03:00
|
|
|
current.backup_mode = parse_backup_mode(arg);
|
2011-12-26 17:21:58 +03:00
|
|
|
}
|