1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-01-26 11:54:25 +02:00
pg_probackup/pg_probackup.c

317 lines
9.3 KiB
C
Raw Normal View History

2016-11-16 20:47:12 +03:00
/*-------------------------------------------------------------------------
*
* pg_probackup.c: Backup/Recovery manager for PostgreSQL.
*
2017-03-01 16:50:07 +03:00
* Portions Copyright (c) 2009-2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Portions Copyright (c) 2015-2017, Postgres Professional
2016-11-16 20:47:12 +03:00
*
*-------------------------------------------------------------------------
*/
#include "pg_probackup.h"
#include "streamutil.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
2017-05-18 12:22:56 +03:00
const char *PROGRAM_VERSION = "1.1.12";
2016-11-16 20:47:12 +03:00
const char *PROGRAM_URL = "https://github.com/postgrespro/pg_probackup";
const char *PROGRAM_EMAIL = "https://github.com/postgrespro/pg_probackup/issues";
/* path configuration */
2017-04-27 14:09:44 +03:00
char *backup_path = NULL;
char *pgdata = NULL;
char arclog_path[MAXPGPATH] = "";
2016-11-16 20:47:12 +03:00
/* directory configuration */
pgBackup current;
2017-04-27 14:09:44 +03:00
ProbackupSubcmd backup_subcmd;
2016-11-16 20:47:12 +03:00
2017-04-27 14:09:44 +03:00
bool help = false;
2017-05-05 13:14:46 +03:00
2017-04-27 14:09:44 +03:00
char *backup_id_string_param = NULL;
bool backup_logs = false;
2017-04-27 14:09:44 +03:00
bool smooth_checkpoint;
int num_threads = 1;
bool stream_wal = false;
bool from_replica = false;
bool progress = false;
bool delete_wal = false;
bool delete_expired = false;
bool apply_to_all = false;
bool force_delete = false;
/* Wait timeout for WAL segment archiving */
uint32 archive_timeout = 300;
2017-04-27 14:09:44 +03:00
uint64 system_identifier = 0;
2017-04-27 14:09:44 +03:00
uint32 retention_redundancy = 0;
uint32 retention_window = 0;
2016-11-16 20:47:12 +03:00
/* restore configuration */
static char *target_time;
static char *target_xid;
static char *target_inclusive;
static TimeLineID target_tli;
static void opt_backup_mode(pgut_option *opt, const char *arg);
2017-04-27 14:09:44 +03:00
static void opt_log_level(pgut_option *opt, const char *arg);
2016-11-16 20:47:12 +03:00
static pgut_option options[] =
{
/* directory options */
2017-04-27 14:09:44 +03:00
{ 'b', 1, "help", &help, SOURCE_CMDLINE },
{ 's', 'D', "pgdata", &pgdata, SOURCE_CMDLINE },
{ 's', 'B', "backup-path", &backup_path, SOURCE_CMDLINE },
2016-11-16 20:47:12 +03:00
/* common options */
2017-04-27 14:09:44 +03:00
{ 'u', 'j', "threads", &num_threads, SOURCE_CMDLINE },
{ 'b', 1, "stream", &stream_wal, SOURCE_CMDLINE },
{ 'b', 2, "progress", &progress, SOURCE_CMDLINE },
{ 's', 'i', "backup-id", &backup_id_string_param, SOURCE_CMDLINE },
2016-11-16 20:47:12 +03:00
/* backup options */
2017-04-27 14:09:44 +03:00
{ 'b', 10, "backup-pg-log", &backup_logs, SOURCE_CMDLINE },
{ '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-04-27 14:09:44 +03:00
{ 'u', 11, "archive-timeout", &archive_timeout, SOURCE_CMDLINE },
{ 'b', 12, "delete-expired", &delete_expired, SOURCE_CMDLINE },
2016-11-16 20:47:12 +03:00
/* restore options */
2017-04-27 14:09:44 +03:00
{ 's', 20, "time", &target_time, SOURCE_CMDLINE },
{ 's', 21, "xid", &target_xid, SOURCE_CMDLINE },
{ 's', 22, "inclusive", &target_inclusive, SOURCE_CMDLINE },
{ 'u', 23, "timeline", &target_tli, SOURCE_CMDLINE },
{ 'f', 'T', "tablespace-mapping", opt_tablespace_map, SOURCE_CMDLINE },
2016-11-16 20:47:12 +03:00
/* delete options */
2017-04-27 14:09:44 +03:00
{ 'b', 30, "wal", &delete_wal, SOURCE_CMDLINE },
{ 'b', 31, "expired", &delete_expired, SOURCE_CMDLINE },
{ 'b', 32, "all", &apply_to_all, SOURCE_CMDLINE },
/* TODO not implemented yet */
2017-04-27 14:09:44 +03:00
{ 'b', 33, "force", &force_delete, SOURCE_CMDLINE },
/* retention options */
{ 'u', 34, "retention-redundancy", &retention_redundancy, SOURCE_CMDLINE },
{ 'u', 35, "retention-window", &retention_window, SOURCE_CMDLINE },
/* logging options */
{ 'f', 40, "log-level", opt_log_level, SOURCE_CMDLINE },
{ 's', 41, "log-filename", &log_filename, SOURCE_CMDLINE },
{ 's', 42, "error-log-filename", &error_log_filename, SOURCE_CMDLINE },
{ 's', 43, "log-directory", &log_directory, SOURCE_CMDLINE },
{ 'u', 44, "log-rotation-size", &log_rotation_size, SOURCE_CMDLINE },
{ 'u', 45, "log-rotation-age", &log_rotation_age, SOURCE_CMDLINE },
/* connection options */
{ 's', 'd', "pgdatabase", &pgut_dbname, SOURCE_CMDLINE },
{ 's', 'h', "pghost", &host, SOURCE_CMDLINE },
{ 's', 'p', "pgport", &port, SOURCE_CMDLINE },
{ 's', 'U', "pguser", &username, SOURCE_CMDLINE },
{ 'B', 'w', "no-password", &prompt_password, SOURCE_CMDLINE },
/* other options */
{ 'U', 50, "system-identifier", &system_identifier, SOURCE_FILE_STRICT },
2016-11-16 20:47:12 +03:00
{ 0 }
};
/*
* Entry point of pg_probackup command.
*/
int
main(int argc, char *argv[])
{
char path[MAXPGPATH];
/* Check if backup_path is directory. */
struct stat stat_buf;
int rc;
2016-11-16 20:47:12 +03:00
/* initialize configuration */
pgBackup_init(&current);
2016-11-16 20:47:12 +03:00
PROGRAM_NAME = get_progname(argv[0]);
set_pglocale_pgservice(argv[0], "pgscripts");
2016-11-16 20:47:12 +03:00
/* Parse subcommands and non-subcommand options */
if (argc > 1)
2016-11-16 20:47:12 +03: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], "set-config") == 0)
backup_subcmd = SET_CONFIG;
else if (strcmp(argv[1], "show-config") == 0)
backup_subcmd = SHOW_CONFIG;
2017-04-20 15:22:18 +03:00
else if (strcmp(argv[1], "--help") == 0
|| strcmp(argv[1], "help") == 0
|| strcmp(argv[1], "-?") == 0)
{
2017-05-05 13:14:46 +03:00
if (argc > 2)
help_command(argv[2]);
else
help_pg_probackup();
}
2017-04-20 15:22:18 +03:00
else if (strcmp(argv[1], "--version") == 0
|| strcmp(argv[1], "version") == 0
|| strcmp(argv[1], "-V") == 0)
{
2017-05-05 13:14:46 +03:00
if (argc == 2)
{
fprintf(stderr, "%s %s\n", PROGRAM_NAME, PROGRAM_VERSION);
exit(0);
}
else if (strcmp(argv[2], "--help") == 0)
help_command(argv[1]);
else
elog(ERROR, "Invalid arguments for \"%s\" subcommand", argv[1]);
}
2016-11-16 20:47:12 +03:00
else
2017-05-05 13:14:46 +03:00
elog(ERROR, "Unknown subcommand");
2016-11-16 20:47:12 +03:00
}
/* Parse command line arguments */
pgut_getopt(argc, argv, options);
2016-11-16 20:47:12 +03:00
2017-05-05 13:14:46 +03:00
if (help)
help_command(argv[2]);
if (backup_path == NULL)
{
/* 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)");
}
2016-11-16 20:47:12 +03:00
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");
2016-11-16 20:47:12 +03:00
/* Do not read options from file or env if we're going to set them */
if (backup_subcmd != SET_CONFIG)
{
/* 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);
2016-11-16 20:47:12 +03:00
}
2017-04-26 17:00:06 +03:00
/*
* We read backup path from command line or from configuration file.
* Do final check.
*/
if (!is_absolute_path(backup_path))
elog(ERROR, "-B, --backup-path must be an absolute path");
2017-04-27 14:09:44 +03:00
/* Set log path */
if (log_filename || error_log_filename)
{
if (log_directory)
join_path_components(log_path, backup_path, log_directory);
else
join_path_components(log_path, backup_path, "log");
}
if (backup_id_string_param != NULL)
{
current.backup_id = base36dec(backup_id_string_param);
if (current.backup_id == 0)
elog(ERROR, "Invalid backup-id");
}
2017-04-26 17:00:06 +03:00
/* Setup stream options. They are used in streamutil.c. */
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 20:47:12 +03:00
2017-04-26 17:00:06 +03:00
/*
* We read pgdata path from command line or from configuration file.
* Do final check.
*/
2016-11-16 20:47:12 +03:00
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)
{
int i;
for (i = 0; pgdata_exclude_dir[i]; i++); /* find first empty slot */
2016-11-16 20:47:12 +03:00
/* Set 'pg_log' in first empty slot */
pgdata_exclude_dir[i] = "pg_log";
}
2016-11-16 20:47:12 +03: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 14:26:58 +03:00
if (num_threads < 1)
num_threads = 1;
2016-11-16 20:47:12 +03:00
/* do actual operation */
switch (backup_subcmd)
{
case INIT:
return do_init();
case BACKUP:
return do_backup();
case RESTORE:
return do_restore_or_validate(current.backup_id,
target_time, target_xid,
target_inclusive, target_tli,
true);
case VALIDATE:
return do_restore_or_validate(current.backup_id,
target_time, target_xid,
target_inclusive, target_tli,
false);
case SHOW:
return do_show(current.backup_id);
case DELETE:
if (delete_expired && backup_id_string_param)
elog(ERROR, "You cannot specify --delete-expired and --backup-id options together");
if (delete_expired)
return do_retention_purge();
else
return do_delete(current.backup_id);
case SHOW_CONFIG:
if (argc > 4)
elog(ERROR, "show-config command doesn't accept any options");
return do_configure(true);
case SET_CONFIG:
if (argc == 4)
elog(ERROR, "set-config command requires at least one option");
return do_configure(false);
}
2016-11-16 20:47:12 +03:00
return 0;
}
static void
opt_backup_mode(pgut_option *opt, const char *arg)
{
current.backup_mode = parse_backup_mode(arg);
}
2017-04-27 14:09:44 +03:00
static void
opt_log_level(pgut_option *opt, const char *arg)
{
log_level = parse_log_level(arg);
2017-05-18 12:07:45 +03:00
log_level_defined = true;
2017-04-27 14:09:44 +03:00
}