2013-01-24 09:35:48 +03:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* catalog.c: backup catalog opration
|
|
|
|
*
|
2017-03-01 15:50:07 +02:00
|
|
|
* Portions Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
|
|
|
* Portions Copyright (c) 2015-2017, Postgres Professional
|
2013-01-24 09:35:48 +03:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2016-11-16 19:34:21 +02:00
|
|
|
#include "pg_probackup.h"
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "pgut/pgut-port.h"
|
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
static pgBackup *read_backup_from_file(const char *path);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
#define BOOL_TO_STR(val) ((val) ? "true" : "false")
|
|
|
|
|
|
|
|
static int lock_fd = -1;
|
|
|
|
|
|
|
|
/*
|
2016-11-16 19:34:21 +02:00
|
|
|
* Lock of the catalog with pg_probackup.conf file and return 0.
|
2013-01-24 09:35:48 +03:00
|
|
|
* If the lock is held by another one, return 1 immediately.
|
|
|
|
*/
|
|
|
|
int
|
2017-02-12 22:42:10 +02:00
|
|
|
catalog_lock(bool check_catalog)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
2017-02-12 22:42:10 +02:00
|
|
|
int ret;
|
|
|
|
char id_path[MAXPGPATH];
|
2013-01-24 09:35:48 +03:00
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
join_path_components(id_path, backup_path, BACKUP_CATALOG_CONF_FILE);
|
2013-01-24 09:35:48 +03:00
|
|
|
lock_fd = open(id_path, O_RDWR);
|
|
|
|
if (lock_fd == -1)
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(errno == ENOENT ? ERROR : ERROR,
|
2016-01-14 09:36:39 +02:00
|
|
|
"cannot open file \"%s\": %s", id_path, strerror(errno));
|
2016-12-08 13:42:32 +02:00
|
|
|
#ifdef __IBMC__
|
|
|
|
ret = lockf(lock_fd, LOCK_EX | LOCK_NB, 0); /* non-blocking */
|
|
|
|
#else
|
2013-01-24 09:35:48 +03:00
|
|
|
ret = flock(lock_fd, LOCK_EX | LOCK_NB); /* non-blocking */
|
2016-12-08 13:42:32 +02:00
|
|
|
#endif
|
2013-01-24 09:35:48 +03:00
|
|
|
if (ret == -1)
|
|
|
|
{
|
|
|
|
if (errno == EWOULDBLOCK)
|
|
|
|
{
|
|
|
|
close(lock_fd);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int errno_tmp = errno;
|
|
|
|
close(lock_fd);
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "cannot lock file \"%s\": %s", id_path,
|
2013-01-24 09:35:48 +03:00
|
|
|
strerror(errno_tmp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
if (check_catalog)
|
|
|
|
{
|
|
|
|
uint64 id;
|
|
|
|
|
|
|
|
Assert(pgdata);
|
|
|
|
|
|
|
|
/* Check system-identifier */
|
|
|
|
id = get_system_identifier(true);
|
|
|
|
if (id != system_identifier)
|
|
|
|
elog(ERROR, "Backup directory was initialized for system id = %ld, but target system id = %ld",
|
|
|
|
system_identifier, id);
|
|
|
|
}
|
|
|
|
|
2013-01-24 09:35:48 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release catalog lock.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
catalog_unlock(void)
|
|
|
|
{
|
|
|
|
close(lock_fd);
|
|
|
|
lock_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a pgBackup which taken at timestamp.
|
|
|
|
* If no backup matches, return NULL.
|
|
|
|
*/
|
|
|
|
pgBackup *
|
2017-02-12 22:42:10 +02:00
|
|
|
read_backup(time_t timestamp)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
|
|
|
pgBackup tmp;
|
2017-02-12 22:42:10 +02:00
|
|
|
char conf_path[MAXPGPATH];
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
tmp.start_time = timestamp;
|
2017-02-12 22:42:10 +02:00
|
|
|
pgBackupGetPath(&tmp, conf_path, lengthof(conf_path), BACKUP_CONF_FILE);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
return read_backup_from_file(conf_path);
|
2013-01-24 09:35:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2016-01-19 13:48:13 +02:00
|
|
|
IsDir(const char *dirpath, const char *entry)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
|
|
|
char path[MAXPGPATH];
|
|
|
|
struct stat st;
|
|
|
|
|
2016-01-19 13:48:13 +02:00
|
|
|
snprintf(path, MAXPGPATH, "%s/%s", dirpath, entry);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create list fo backups started between begin and end from backup catalog.
|
|
|
|
* If range was NULL, all of backup are listed.
|
|
|
|
* The list is sorted in order of descending start time.
|
|
|
|
*/
|
|
|
|
parray *
|
2016-10-31 17:19:11 +02:00
|
|
|
catalog_get_backup_list(time_t backup_id)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
|
|
|
DIR *date_dir = NULL;
|
|
|
|
struct dirent *date_ent = NULL;
|
|
|
|
DIR *time_dir = NULL;
|
2016-11-07 16:35:53 +02:00
|
|
|
char backups_path[MAXPGPATH];
|
2013-01-24 09:35:48 +03:00
|
|
|
char date_path[MAXPGPATH];
|
|
|
|
parray *backups = NULL;
|
|
|
|
pgBackup *backup = NULL;
|
|
|
|
|
|
|
|
/* open backup root directory */
|
2016-11-07 16:35:53 +02:00
|
|
|
join_path_components(backups_path, backup_path, BACKUPS_DIR);
|
|
|
|
date_dir = opendir(backups_path);
|
2013-01-24 09:35:48 +03:00
|
|
|
if (date_dir == NULL)
|
|
|
|
{
|
2016-11-07 16:35:53 +02:00
|
|
|
elog(WARNING, "cannot open directory \"%s\": %s", backups_path,
|
2013-01-24 09:35:48 +03:00
|
|
|
strerror(errno));
|
|
|
|
goto err_proc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* scan date/time directories and list backups in the range */
|
|
|
|
backups = parray_new();
|
|
|
|
for (; (date_ent = readdir(date_dir)) != NULL; errno = 0)
|
|
|
|
{
|
2016-10-31 17:19:11 +02:00
|
|
|
char ini_path[MAXPGPATH];
|
|
|
|
|
2013-01-24 09:35:48 +03:00
|
|
|
/* skip not-directory entries and hidden entries */
|
2016-11-07 16:35:53 +02:00
|
|
|
if (!IsDir(backups_path, date_ent->d_name) || date_ent->d_name[0] == '.')
|
2013-01-24 09:35:48 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* open subdirectory (date directory) and search time directory */
|
2016-11-07 16:35:53 +02:00
|
|
|
join_path_components(date_path, backups_path, date_ent->d_name);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
2016-10-31 17:19:11 +02:00
|
|
|
/* read backup information from backup.ini */
|
2017-02-12 22:42:10 +02:00
|
|
|
snprintf(ini_path, MAXPGPATH, "%s/%s", date_path, BACKUP_CONF_FILE);
|
|
|
|
backup = read_backup_from_file(ini_path);
|
2016-01-19 13:48:13 +02:00
|
|
|
|
2016-10-31 17:19:11 +02:00
|
|
|
/* ignore corrupted backup */
|
|
|
|
if (backup)
|
|
|
|
{
|
|
|
|
if (backup_id != 0 && backup_id != backup->start_time)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
2016-10-31 17:19:11 +02:00
|
|
|
pgBackupFree(backup);
|
|
|
|
continue;
|
2013-01-24 09:35:48 +03:00
|
|
|
}
|
2016-10-31 17:19:11 +02:00
|
|
|
parray_append(backups, backup);
|
|
|
|
backup = NULL;
|
2013-01-24 09:35:48 +03:00
|
|
|
}
|
|
|
|
if (errno && errno != ENOENT)
|
|
|
|
{
|
2016-01-14 09:36:39 +02:00
|
|
|
elog(WARNING, "cannot read date directory \"%s\": %s",
|
2013-01-24 09:35:48 +03:00
|
|
|
date_ent->d_name, strerror(errno));
|
|
|
|
goto err_proc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (errno)
|
|
|
|
{
|
2016-01-14 09:36:39 +02:00
|
|
|
elog(WARNING, "cannot read backup root directory \"%s\": %s",
|
2016-11-07 16:35:53 +02:00
|
|
|
backups_path, strerror(errno));
|
2013-01-24 09:35:48 +03:00
|
|
|
goto err_proc;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(date_dir);
|
|
|
|
date_dir = NULL;
|
|
|
|
|
|
|
|
parray_qsort(backups, pgBackupCompareIdDesc);
|
|
|
|
|
|
|
|
return backups;
|
|
|
|
|
|
|
|
err_proc:
|
|
|
|
if (time_dir)
|
|
|
|
closedir(time_dir);
|
|
|
|
if (date_dir)
|
|
|
|
closedir(date_dir);
|
|
|
|
if (backup)
|
|
|
|
pgBackupFree(backup);
|
|
|
|
if (backups)
|
|
|
|
parray_walk(backups, pgBackupFree);
|
|
|
|
parray_free(backups);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the last completed database backup from the backup list.
|
|
|
|
*/
|
|
|
|
pgBackup *
|
2013-12-24 23:27:25 +03:00
|
|
|
catalog_get_last_data_backup(parray *backup_list, TimeLineID tli)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
pgBackup *backup = NULL;
|
|
|
|
|
|
|
|
/* backup_list is sorted in order of descending ID */
|
|
|
|
for (i = 0; i < parray_num(backup_list); i++)
|
|
|
|
{
|
|
|
|
backup = (pgBackup *) parray_get(backup_list, i);
|
|
|
|
|
2013-12-15 18:30:49 +03:00
|
|
|
/*
|
|
|
|
* We need completed database backup in the case of a full or
|
2014-01-30 09:58:55 +03:00
|
|
|
* differential backup on current timeline.
|
2013-12-15 18:30:49 +03:00
|
|
|
*/
|
|
|
|
if (backup->status == BACKUP_STATUS_OK &&
|
2013-12-24 23:27:25 +03:00
|
|
|
backup->tli == tli &&
|
2014-01-30 09:58:55 +03:00
|
|
|
(backup->backup_mode == BACKUP_MODE_DIFF_PAGE ||
|
2016-02-27 20:07:55 +02:00
|
|
|
backup->backup_mode == BACKUP_MODE_DIFF_PTRACK ||
|
2013-12-15 18:30:49 +03:00
|
|
|
backup->backup_mode == BACKUP_MODE_FULL))
|
2013-01-24 09:35:48 +03:00
|
|
|
return backup;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create backup directory in $BACKUP_PATH */
|
|
|
|
int
|
|
|
|
pgBackupCreateDir(pgBackup *backup)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char path[MAXPGPATH];
|
2014-01-24 16:36:31 +03:00
|
|
|
char *subdirs[] = { DATABASE_DIR, NULL };
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
pgBackupGetPath(backup, path, lengthof(path), NULL);
|
2017-02-25 14:12:07 +02:00
|
|
|
|
|
|
|
if (!dir_is_empty(path))
|
|
|
|
elog(ERROR, "backup destination is not empty \"%s\"", path);
|
|
|
|
|
2013-01-24 09:35:48 +03:00
|
|
|
dir_create_dir(path, DIR_PERMISSION);
|
|
|
|
|
|
|
|
/* create directories for actual backup files */
|
|
|
|
for (i = 0; subdirs[i]; i++)
|
|
|
|
{
|
|
|
|
pgBackupGetPath(backup, path, lengthof(path), subdirs[i]);
|
|
|
|
dir_create_dir(path, DIR_PERMISSION);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write configuration section of backup.in to stream "out".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pgBackupWriteConfigSection(FILE *out, pgBackup *backup)
|
|
|
|
{
|
2016-02-27 20:07:55 +02:00
|
|
|
static const char *modes[] = { "", "PAGE", "PTRACK", "FULL"};
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
fprintf(out, "# configuration\n");
|
|
|
|
fprintf(out, "BACKUP_MODE=%s\n", modes[backup->backup_mode]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write result section of backup.in to stream "out".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pgBackupWriteResultSection(FILE *out, pgBackup *backup)
|
|
|
|
{
|
|
|
|
char timestamp[20];
|
|
|
|
|
|
|
|
fprintf(out, "# result\n");
|
|
|
|
fprintf(out, "TIMELINEID=%d\n", backup->tli);
|
2013-12-09 21:21:07 +03:00
|
|
|
fprintf(out, "START_LSN=%x/%08x\n",
|
|
|
|
(uint32) (backup->start_lsn >> 32),
|
|
|
|
(uint32) backup->start_lsn);
|
|
|
|
fprintf(out, "STOP_LSN=%x/%08x\n",
|
|
|
|
(uint32) (backup->stop_lsn >> 32),
|
|
|
|
(uint32) backup->stop_lsn);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
time2iso(timestamp, lengthof(timestamp), backup->start_time);
|
|
|
|
fprintf(out, "START_TIME='%s'\n", timestamp);
|
|
|
|
if (backup->end_time > 0)
|
|
|
|
{
|
|
|
|
time2iso(timestamp, lengthof(timestamp), backup->end_time);
|
|
|
|
fprintf(out, "END_TIME='%s'\n", timestamp);
|
|
|
|
}
|
2016-12-20 15:22:08 +02:00
|
|
|
fprintf(out, "RECOVERY_XID=" XID_FMT "\n", backup->recovery_xid);
|
2013-01-24 09:35:48 +03:00
|
|
|
if (backup->recovery_time > 0)
|
|
|
|
{
|
|
|
|
time2iso(timestamp, lengthof(timestamp), backup->recovery_time);
|
|
|
|
fprintf(out, "RECOVERY_TIME='%s'\n", timestamp);
|
|
|
|
}
|
|
|
|
|
2014-01-09 22:11:27 +03:00
|
|
|
if (backup->data_bytes != BYTES_INVALID)
|
|
|
|
fprintf(out, "DATA_BYTES=" INT64_FORMAT "\n",
|
|
|
|
backup->data_bytes);
|
2013-01-24 09:35:48 +03:00
|
|
|
fprintf(out, "BLOCK_SIZE=%u\n", backup->block_size);
|
|
|
|
fprintf(out, "XLOG_BLOCK_SIZE=%u\n", backup->wal_block_size);
|
2016-07-07 17:38:56 +02:00
|
|
|
fprintf(out, "CHECKSUM_VERSION=%u\n", backup->checksum_version);
|
2016-11-01 16:25:46 +02:00
|
|
|
fprintf(out, "STREAM=%u\n", backup->stream);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
fprintf(out, "STATUS=%s\n", status2str(backup->status));
|
2016-12-19 17:41:41 +02:00
|
|
|
if (backup->parent_backup != 0)
|
|
|
|
{
|
|
|
|
char *parent_backup = base36enc(backup->parent_backup);
|
|
|
|
fprintf(out, "PARENT_BACKUP='%s'\n", parent_backup);
|
|
|
|
free(parent_backup);
|
|
|
|
}
|
2013-01-24 09:35:48 +03:00
|
|
|
}
|
|
|
|
|
2017-02-15 19:44:06 +02:00
|
|
|
/* create backup.conf */
|
2013-01-24 09:35:48 +03:00
|
|
|
void
|
|
|
|
pgBackupWriteIni(pgBackup *backup)
|
|
|
|
{
|
|
|
|
FILE *fp = NULL;
|
|
|
|
char ini_path[MAXPGPATH];
|
|
|
|
|
2017-02-12 22:42:10 +02:00
|
|
|
pgBackupGetPath(backup, ini_path, lengthof(ini_path), BACKUP_CONF_FILE);
|
2013-01-24 09:35:48 +03:00
|
|
|
fp = fopen(ini_path, "wt");
|
|
|
|
if (fp == NULL)
|
2017-02-15 19:44:06 +02:00
|
|
|
elog(ERROR, "cannot open configuration file \"%s\": %s", ini_path,
|
2013-01-24 09:35:48 +03:00
|
|
|
strerror(errno));
|
|
|
|
|
|
|
|
/* configuration section */
|
|
|
|
pgBackupWriteConfigSection(fp, backup);
|
|
|
|
|
|
|
|
/* result section */
|
|
|
|
pgBackupWriteResultSection(fp, backup);
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read backup.ini and create pgBackup.
|
|
|
|
* - Comment starts with ';'.
|
|
|
|
* - Do not care section.
|
|
|
|
*/
|
|
|
|
static pgBackup *
|
2017-02-12 22:42:10 +02:00
|
|
|
read_backup_from_file(const char *path)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
|
|
|
pgBackup *backup;
|
|
|
|
char *backup_mode = NULL;
|
|
|
|
char *start_lsn = NULL;
|
|
|
|
char *stop_lsn = NULL;
|
|
|
|
char *status = NULL;
|
2016-12-19 17:41:41 +02:00
|
|
|
char *parent_backup = NULL;
|
2013-01-24 09:35:48 +03:00
|
|
|
int i;
|
|
|
|
|
|
|
|
pgut_option options[] =
|
|
|
|
{
|
2017-02-12 22:42:10 +02:00
|
|
|
{'s', 0, "backup-mode", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'u', 0, "timelineid", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'s', 0, "start-lsn", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'s', 0, "stop-lsn", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'t', 0, "start-time", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'t', 0, "end-time", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'U', 0, "recovery-xid", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'t', 0, "recovery-time", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'I', 0, "data-bytes", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'u', 0, "block-size", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'u', 0, "xlog-block-size", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'u', 0, "checksum_version", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'u', 0, "stream", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'s', 0, "status", NULL, SOURCE_FILE_STRICT},
|
|
|
|
{'s', 0, "parent_backup", NULL, SOURCE_FILE_STRICT},
|
2016-12-07 15:28:48 +02:00
|
|
|
{0}
|
2013-01-24 09:35:48 +03:00
|
|
|
};
|
|
|
|
|
2016-01-14 09:36:39 +02:00
|
|
|
if (access(path, F_OK) != 0)
|
2013-01-24 09:35:48 +03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
backup = pgut_new(pgBackup);
|
2017-02-12 22:42:10 +02:00
|
|
|
init_backup(backup);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
options[i++].var = &backup_mode;
|
|
|
|
options[i++].var = &backup->tli;
|
|
|
|
options[i++].var = &start_lsn;
|
|
|
|
options[i++].var = &stop_lsn;
|
|
|
|
options[i++].var = &backup->start_time;
|
|
|
|
options[i++].var = &backup->end_time;
|
|
|
|
options[i++].var = &backup->recovery_xid;
|
|
|
|
options[i++].var = &backup->recovery_time;
|
2014-01-09 22:11:27 +03:00
|
|
|
options[i++].var = &backup->data_bytes;
|
2013-01-24 09:35:48 +03:00
|
|
|
options[i++].var = &backup->block_size;
|
|
|
|
options[i++].var = &backup->wal_block_size;
|
2016-07-07 17:38:56 +02:00
|
|
|
options[i++].var = &backup->checksum_version;
|
2016-11-01 16:25:46 +02:00
|
|
|
options[i++].var = &backup->stream;
|
2013-01-24 09:35:48 +03:00
|
|
|
options[i++].var = &status;
|
2016-12-19 17:41:41 +02:00
|
|
|
options[i++].var = &parent_backup;
|
2013-01-24 09:35:48 +03:00
|
|
|
Assert(i == lengthof(options) - 1);
|
|
|
|
|
2016-01-19 05:41:30 +02:00
|
|
|
pgut_readopt(path, options, ERROR);
|
2013-01-24 09:35:48 +03:00
|
|
|
|
|
|
|
if (backup_mode)
|
|
|
|
{
|
2013-12-15 18:50:36 +03:00
|
|
|
backup->backup_mode = parse_backup_mode(backup_mode);
|
2013-01-24 09:35:48 +03:00
|
|
|
free(backup_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start_lsn)
|
|
|
|
{
|
2013-12-09 21:21:07 +03:00
|
|
|
uint32 xlogid;
|
|
|
|
uint32 xrecoff;
|
|
|
|
|
|
|
|
if (sscanf(start_lsn, "%X/%X", &xlogid, &xrecoff) == 2)
|
|
|
|
backup->start_lsn = (XLogRecPtr) ((uint64) xlogid << 32) | xrecoff;
|
2013-01-24 09:35:48 +03:00
|
|
|
else
|
2016-01-14 09:36:39 +02:00
|
|
|
elog(WARNING, "invalid START_LSN \"%s\"", start_lsn);
|
2013-01-24 09:35:48 +03:00
|
|
|
free(start_lsn);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stop_lsn)
|
|
|
|
{
|
2013-12-09 21:21:07 +03:00
|
|
|
uint32 xlogid;
|
|
|
|
uint32 xrecoff;
|
|
|
|
|
|
|
|
if (sscanf(stop_lsn, "%X/%X", &xlogid, &xrecoff) == 2)
|
|
|
|
backup->stop_lsn = (XLogRecPtr) ((uint64) xlogid << 32) | xrecoff;
|
2013-01-24 09:35:48 +03:00
|
|
|
else
|
2016-01-14 09:36:39 +02:00
|
|
|
elog(WARNING, "invalid STOP_LSN \"%s\"", stop_lsn);
|
2013-01-24 09:35:48 +03:00
|
|
|
free(stop_lsn);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
{
|
|
|
|
if (strcmp(status, "OK") == 0)
|
|
|
|
backup->status = BACKUP_STATUS_OK;
|
|
|
|
else if (strcmp(status, "RUNNING") == 0)
|
|
|
|
backup->status = BACKUP_STATUS_RUNNING;
|
|
|
|
else if (strcmp(status, "ERROR") == 0)
|
|
|
|
backup->status = BACKUP_STATUS_ERROR;
|
|
|
|
else if (strcmp(status, "DELETING") == 0)
|
|
|
|
backup->status = BACKUP_STATUS_DELETING;
|
|
|
|
else if (strcmp(status, "DELETED") == 0)
|
|
|
|
backup->status = BACKUP_STATUS_DELETED;
|
|
|
|
else if (strcmp(status, "DONE") == 0)
|
|
|
|
backup->status = BACKUP_STATUS_DONE;
|
|
|
|
else if (strcmp(status, "CORRUPT") == 0)
|
|
|
|
backup->status = BACKUP_STATUS_CORRUPT;
|
|
|
|
else
|
2016-01-14 09:36:39 +02:00
|
|
|
elog(WARNING, "invalid STATUS \"%s\"", status);
|
2013-01-24 09:35:48 +03:00
|
|
|
free(status);
|
|
|
|
}
|
|
|
|
|
2016-12-19 17:41:41 +02:00
|
|
|
if (parent_backup)
|
|
|
|
{
|
|
|
|
backup->parent_backup = base36dec(parent_backup);
|
|
|
|
free(parent_backup);
|
|
|
|
}
|
|
|
|
|
2013-01-24 09:35:48 +03:00
|
|
|
return backup;
|
|
|
|
}
|
|
|
|
|
|
|
|
BackupMode
|
2013-12-15 18:50:36 +03:00
|
|
|
parse_backup_mode(const char *value)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
|
|
|
const char *v = value;
|
|
|
|
size_t len;
|
|
|
|
|
2013-12-15 18:50:36 +03:00
|
|
|
/* Skip all spaces detected */
|
|
|
|
while (IsSpace(*v))
|
|
|
|
v++;
|
2013-01-24 09:35:48 +03:00
|
|
|
len = strlen(v);
|
|
|
|
|
2014-01-30 09:58:55 +03:00
|
|
|
if (len > 0 && pg_strncasecmp("full", v, strlen("full")) == 0)
|
2013-01-24 09:35:48 +03:00
|
|
|
return BACKUP_MODE_FULL;
|
2014-01-30 09:58:55 +03:00
|
|
|
else if (len > 0 && pg_strncasecmp("page", v, strlen("page")) == 0)
|
|
|
|
return BACKUP_MODE_DIFF_PAGE;
|
2016-02-27 20:07:55 +02:00
|
|
|
else if (len > 0 && pg_strncasecmp("ptrack", v, strlen("ptrack")) == 0)
|
|
|
|
return BACKUP_MODE_DIFF_PTRACK;
|
2013-01-24 09:35:48 +03:00
|
|
|
|
2013-12-15 18:50:36 +03:00
|
|
|
/* Backup mode is invalid, so leave with an error */
|
2016-01-19 05:41:30 +02:00
|
|
|
elog(ERROR, "invalid backup-mode \"%s\"", value);
|
2013-01-24 09:35:48 +03:00
|
|
|
return BACKUP_MODE_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free pgBackup object */
|
|
|
|
void
|
|
|
|
pgBackupFree(void *backup)
|
|
|
|
{
|
|
|
|
free(backup);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare two pgBackup with their IDs (start time) in ascending order */
|
|
|
|
int
|
|
|
|
pgBackupCompareId(const void *l, const void *r)
|
|
|
|
{
|
|
|
|
pgBackup *lp = *(pgBackup **)l;
|
|
|
|
pgBackup *rp = *(pgBackup **)r;
|
|
|
|
|
|
|
|
if (lp->start_time > rp->start_time)
|
|
|
|
return 1;
|
|
|
|
else if (lp->start_time < rp->start_time)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare two pgBackup with their IDs in descending order */
|
|
|
|
int
|
|
|
|
pgBackupCompareIdDesc(const void *l, const void *r)
|
|
|
|
{
|
|
|
|
return -pgBackupCompareId(l, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct absolute path of the backup directory.
|
|
|
|
* If subdir is not NULL, it will be appended after the path.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pgBackupGetPath(const pgBackup *backup, char *path, size_t len, const char *subdir)
|
|
|
|
{
|
2016-10-31 17:19:11 +02:00
|
|
|
char *datetime;
|
2013-01-24 09:35:48 +03:00
|
|
|
|
2016-10-31 17:19:11 +02:00
|
|
|
datetime = base36enc(backup->start_time);
|
2013-01-24 09:35:48 +03:00
|
|
|
if (subdir)
|
2016-11-07 16:35:53 +02:00
|
|
|
snprintf(path, len, "%s/%s/%s/%s", backup_path, BACKUPS_DIR, datetime, subdir);
|
2013-01-24 09:35:48 +03:00
|
|
|
else
|
2016-11-07 16:35:53 +02:00
|
|
|
snprintf(path, len, "%s/%s/%s", backup_path, BACKUPS_DIR, datetime);
|
2016-10-31 17:19:11 +02:00
|
|
|
free(datetime);
|
2017-02-25 14:12:07 +02:00
|
|
|
|
|
|
|
make_native_path(path);
|
2013-01-24 09:35:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-02-12 22:42:10 +02:00
|
|
|
init_backup(pgBackup *backup)
|
2013-01-24 09:35:48 +03:00
|
|
|
{
|
|
|
|
backup->backup_mode = BACKUP_MODE_INVALID;
|
|
|
|
backup->status = BACKUP_STATUS_INVALID;
|
|
|
|
backup->tli = 0;
|
2013-12-09 21:21:07 +03:00
|
|
|
backup->start_lsn = 0;
|
|
|
|
backup->stop_lsn = 0;
|
2013-01-24 09:35:48 +03:00
|
|
|
backup->start_time = (time_t) 0;
|
|
|
|
backup->end_time = (time_t) 0;
|
|
|
|
backup->recovery_xid = 0;
|
|
|
|
backup->recovery_time = (time_t) 0;
|
2014-01-09 22:11:27 +03:00
|
|
|
backup->data_bytes = BYTES_INVALID;
|
2016-11-01 16:25:46 +02:00
|
|
|
backup->stream = false;
|
2016-12-19 17:41:41 +02:00
|
|
|
backup->parent_backup = 0;
|
2013-01-24 09:35:48 +03:00
|
|
|
}
|